카테고리 : MySQL/기술노트

컬럼 자료형(Column type) 변경의 위험: 암묵적 변환·잘림·인덱스 재구축

MySQL 컬럼 자료형 변경에서 암묵적 변환, 데이터 잘림, 인덱스 재구축을 구분하고 SQL mode와 DDL 알고리즘으로 변경 위험을 검증한다.

저자: MySQL 기술 노트 작성: 2026.10.16 약 9분 5,163자
다운로드

자료형 변경은 저장 공간을 늘리거나 줄이는 작업에 그치지 않는다. 같은 값이 비교되는 규칙, 애플리케이션이 전송하는 바인드 타입, 인덱스 탐색 조건, 기존 행을 다시 기록하는 방식이 함께 달라진다. ALTER TABLE이 성공했다는 사실은 데이터 의미와 서비스 호환성이 보존되었다는 증거가 아니다.

이 글은 MySQL 8.0 이상 InnoDB를 대상으로 비교 시점의 암묵적 변환, 저장 시점의 손실, 물리적 재구축 비용을 분리한다. 예제는 전용 테스트 스키마에서 실행해야 하며 운영 테이블에 그대로 적용해서는 안 된다. 공개 실행 결과는 폐기용 MySQL 8.0 인스턴스에서 얻은 기능 검증 결과다. 대용량 수행 시간, 복제 지연, Aurora 클러스터 동작은 이 실험의 검증 범위가 아니다.

1. 변경 검토는 세 가지 계약으로 나눈다

첫째는 값의 계약이다. '0012'가 주문 코드라면 숫자 12와 다른 의미를 가질 수 있다. VARCHAR를 정수로 바꾸어 앞의 0을 잃어도 SQL 오류가 없을 수 있다. 음수 허용 여부, 소수점 자릿수, 시간대, 문자 정규화와 대소문자 구분까지 업무 규칙으로 명시해야 한다.

둘째는 질의의 계약이다. 문자열과 숫자를 비교하면 MySQL의 타입 변환 규칙이 적용된다. 저장된 값이 변하지 않아도 조회 결과와 접근 경로가 달라질 수 있다. 자료형을 바꾼 뒤에도 드라이버가 이전 타입으로 파라미터를 바인딩하면 성능 회귀가 남는다. 대표 SQL을 리터럴로 실행하는 것만으로는 실제 애플리케이션 경로가 검증되지 않는다.

셋째는 운영의 계약이다. INT에서 BIGINT로 범위를 넓히는 일은 값 관점에서는 안전할 수 있지만, 일반적인 InnoDB 컬럼 타입 변경에는 COPY가 필요하다. 반대로 지원 조건 안의 VARCHAR 길이 증가는 INPLACE로 데이터 복사 없이 처리될 수 있다. 값의 안전성과 실행 알고리즘을 같은 기준으로 판단하면 안 된다.

flowchart TD
    A["컬럼 변경 요청"] --> B["값 범위·표현·중복·NULL 검사"]
    B --> C{"변환 뒤 업무 의미 보존?"}
    C -- "아니오" --> D["데이터 정제 또는 새 컬럼 병행"]
    C -- "예" --> E["바인드 타입·조회·인덱스 검증"]
    E --> F["목표 버전에서 알고리즘 명시 시험"]
    F --> G{"잠금·공간·복제 예산 충족?"}
    G -- "아니오" --> H["작업 창 또는 점진적 전환 재설계"]
    G -- "예" --> I["백업·중단 기준 확보 후 변경"]
    I --> J["전체 값 대조·실제 업무 경로 검증"]

2. 암묵적 변환: 스키마를 바꾸기 전에도 조회 의미가 바뀐다

문자열 컬럼을 숫자 리터럴과 비교하면 여러 문자열이 동일한 숫자로 해석될 수 있다. '0012', '12', '12x'를 모두 숫자 12와 비교하는 것과 문자열 '12'를 찾는 것은 다른 질의다. 숫자 접두부만 변환되는 경우에는 경고가 발생할 수 있다.

다음 예제는 strict mode에서도 일반 SELECT의 잘못된 숫자 변환이 반드시 오류로 중단되는 것은 아니라는 점을 확인한다. SHOW WARNINGS는 바로 앞 문장의 경고를 읽기 위해 조회 직후 실행한다. 이 예제에서는 경고 자체가 설명 대상이므로 결과에 남긴다.

SET SESSION sql_mode = 'STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION';
CREATE TABLE type_compare (
  id INT PRIMARY KEY,
  code VARCHAR(12) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  KEY idx_code (code)
) ENGINE=InnoDB;
INSERT INTO type_compare VALUES (1,'0012'),(2,'12'),(3,'12x'),(4,'13');
SELECT id, code FROM type_compare WHERE code = 12 ORDER BY id;
SHOW WARNINGS;
SELECT id, code FROM type_compare WHERE code = '12' ORDER BY id;
DROP TABLE type_compare;

실행 결과(MySQL 8.0.x):

준비·정리 문장과 일부 긴 SQL 표기를 줄인 발췌다. 조회 결과의 행과 열은 그대로 유지했다.

mysql> CREATE TABLE type_compare (...);
Query OK, 0 rows affected

mysql> INSERT INTO type_compare VALUES (1,'0012'),(2,'12'),(3,'12x'),(4,'13');
Query OK, 4 rows affected
Records: 4  Duplicates: 0  Warnings: 0

mysql> SELECT id, code FROM type_compare WHERE code = 12 ORDER BY id;
+----+------+
| id | code |
+----+------+
|  1 | 0012 |
|  2 | 12   |
|  3 | 12x  |
+----+------+
3 rows in set, 1 warning

mysql> SHOW WARNINGS;
+---------+------+-----------------------------------------+
| Level   | Code | Message                                 |
+---------+------+-----------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '12x' |
+---------+------+-----------------------------------------+
1 row in set

mysql> SELECT id, code FROM type_compare WHERE code = '12' ORDER BY id;
+----+------+
| id | code |
+----+------+
|  2 | 12   |
+----+------+
1 row in set

문자열 인덱스는 문자열 순서로 정렬되어 있다. 숫자 비교에서는 숫자 12로 변환되는 문자열 하나만을 단순한 문자열 키로 지정할 수 없으므로, 같은 의미의 인덱스 동등 탐색을 기대할 수 없다. 실행 계획이 index로 표시되더라도 전체 인덱스 스캔일 수 있으며 ref나 const 탐색과 구별해야 한다. 이 글의 축소 예제는 결과 의미와 경고를 확인하며, 실제 인덱스 선택은 운영과 유사한 분포에서 EXPLAIN으로 별도 검증한다.

수정 방향은 컬럼에 무조건 CAST()를 씌우는 것이 아니다. 문자열 식별자는 문자열로 바인딩하고, 정수 도메인이라면 입력 검증과 스키마를 함께 정수로 맞춘다. 매우 큰 정수와 문자열의 비교는 부동소수점 변환에 따른 정밀도 문제도 고려해야 한다. 식별자의 의미가 확정되지 않은 상태에서 자동 숫자 변환을 정제 규칙으로 사용하지 않는다.

3. 잘림: strict mode의 거부와 비엄격 모드의 성공을 구별한다

문자열 길이를 줄이는 작업은 현재 데이터가 새 길이에 모두 들어가야 한다. strict mode는 손실성 변환을 막는 중요한 방어선이지만 업무 의미를 판정하는 검증기는 아니다. 자료형 변경 뒤에도 앞자리 0이 필요했는지, 금액의 소수 자릿수 축소가 허용되는지는 별도로 결정해야 한다.

다음 실험에서는 VARCHAR(8)의 'ABCDE'를 VARCHAR(3)으로 줄인다. 저장 프로시저의 예외 처리기는 예상된 거부를 관측하기 위한 실험 장치다. 운영 DDL 오류를 무시하고 계속 진행하는 패턴으로 사용하지 않는다. 저장 프로그램은 생성 시점의 SQL mode를 보관하므로 프로시저 생성 전에 strict mode를 설정한다.

SET SESSION sql_mode = 'STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION';
CREATE TABLE type_shrink (
  id INT PRIMARY KEY,
  code VARCHAR(8) NOT NULL
) ENGINE=InnoDB;
INSERT INTO type_shrink VALUES (1,'ABCDE');
DELIMITER //
CREATE PROCEDURE try_strict_shrink()
BEGIN
  DECLARE CONTINUE HANDLER FOR 1265
    GET DIAGNOSTICS CONDITION 1 @shrink_errno = MYSQL_ERRNO;
  SET @shrink_errno = 0;
  ALTER TABLE type_shrink MODIFY code VARCHAR(3) NOT NULL, ALGORITHM=COPY;
END//
DELIMITER ;
CALL try_strict_shrink();
SELECT @shrink_errno AS strict_error, code,
       CHARACTER_MAXIMUM_LENGTH AS max_chars
FROM type_shrink
JOIN information_schema.COLUMNS
  ON TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'type_shrink'
 AND COLUMN_NAME = 'code';
SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION';
ALTER TABLE type_shrink MODIFY code VARCHAR(3) NOT NULL, ALGORITHM=COPY;
SHOW WARNINGS;
SELECT id, code FROM type_shrink;
ALTER TABLE type_shrink MODIFY code VARCHAR(8) NOT NULL, ALGORITHM=INPLACE;
SELECT id, code FROM type_shrink;
SET SESSION sql_mode = 'STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION';
DROP PROCEDURE try_strict_shrink;
DROP TABLE type_shrink;

실행 결과(MySQL 8.0.x):

준비·정리 문장과 일부 긴 SQL 표기를 줄인 발췌다. 조회 결과의 행과 열은 그대로 유지했다.

mysql> CREATE TABLE type_shrink (...);
Query OK, 0 rows affected

mysql> INSERT INTO type_shrink VALUES (1,'ABCDE');
Query OK, 1 row affected

mysql> SELECT @shrink_errno AS strict_error, code ...;
+--------------+-------+-----------+
| strict_error | code  | max_chars |
+--------------+-------+-----------+
|         1265 | ABCDE |         8 |
+--------------+-------+-----------+
1 row in set

mysql> ALTER TABLE type_shrink MODIFY code VARCHAR(3) NOT NULL, ALGORITHM=COPY;
Query OK, 1 row affected, 1 warning
Records: 1  Duplicates: 0  Warnings: 1

mysql> SHOW WARNINGS;
+---------+------+-------------------------------------------+
| Level   | Code | Message                                   |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'code' at row 1 |
+---------+------+-------------------------------------------+
1 row in set

mysql> SELECT id, code FROM type_shrink;
+----+------+
| id | code |
+----+------+
|  1 | ABC  |
+----+------+
1 row in set

mysql> ALTER TABLE type_shrink MODIFY code VARCHAR(8) NOT NULL, ALGORITHM=INPLACE;
Query OK, 0 rows affected
Records: 0  Duplicates: 0  Warnings: 0

mysql> SELECT id, code FROM type_shrink;
+----+------+
| id | code |
+----+------+
|  1 | ABC  |
+----+------+
1 row in set

첫 번째 시도는 오류를 포착한 뒤 원래 길이와 값이 보존되었는지 확인한다. 비엄격 모드의 두 번째 시도는 경고와 함께 잘린 값을 남긴다. 마지막으로 길이를 다시 늘려도 손실된 문자는 복구되지 않는지 확인한다. 비엄격 모드를 사용한 부분은 손실을 보여 주기 위한 실험이며 운영 권장 설정이 아니다.

변경 전에 검사할 값

  • 문자열 축소: CHAR_LENGTH()로 문자 수, OCTET_LENGTH()로 실제 바이트 길이를 확인한다. 문자 수 상한과 인덱스 바이트 상한은 별개의 제약이다.
  • 정수 축소·부호 변경: 최솟값·최댓값·음수·NULL을 확인한다. UNSIGNED에서 signed로 바꾸는 일도 반드시 안전한 확장은 아니다.
  • DECIMAL 변경: 정수부의 허용 범위와 소수부의 반올림을 각각 검사한다. 오류가 없다는 사실과 금액이 완전히 일치한다는 사실은 다르다.
  • 문자열에서 숫자로 전환: 숫자가 아닌 문자, 빈 문자열, 공백, 앞자리 0, 범위 초과를 분리한다. 서로 다른 원래 값이 변환 뒤 같은 값으로 합쳐지는지도 검사한다.
  • collation 변경: 대소문자·악센트·후행 공백의 비교 방식이 바뀔 수 있다. UNIQUE 키가 새로운 비교 규칙에서 충돌하지 않는지 확인한다.

검사는 표본 확인만으로 끝내지 않는다. 큰 테이블에서는 주키 범위별 집계나 복제한 검증 환경을 활용하고, 검사 후 유입되는 신규 데이터에도 같은 제약을 적용한다. 사전 검사에서 부적합 행이 없더라도 본 변경까지 구버전 애플리케이션이 계속 쓰면 안전성은 깨질 수 있다.

4. 인덱스 재구축: 값의 확장도 물리적 작업은 가볍지 않다

일반적인 InnoDB 컬럼 자료형 변경에서는 ALGORITHM=INPLACE를 명시하면 지원하지 않는 작업으로 거부된다. 알고리즘을 생략하면 서버가 이용 가능한 방식을 선택하므로 의도하지 않은 COPY가 실행될 위험이 있다. 실패를 허용하더라도 실행 방식을 고정하는 지정은 작업량의 한계를 지키는 데 유효하다.

다음은 기본 키 id를 INT에서 BIGINT로 바꾼다. 먼저 INPLACE에서 거부되는지 확인하고, 별도 문장으로 COPY를 명시해 실행한다. 운영에서 이 단계를 자동 대체 실행으로 구현해서는 안 된다. COPY는 쓰기 중단을 전제로 별도 승인해야 한다.

CREATE TABLE type_rebuild (
  id INT NOT NULL,
  code VARCHAR(12) NOT NULL,
  PRIMARY KEY (id),
  KEY idx_code (code)
) ENGINE=InnoDB;
INSERT INTO type_rebuild VALUES (1,'A'),(2,'B');
DELIMITER //
CREATE PROCEDURE try_type_inplace()
BEGIN
  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
    GET DIAGNOSTICS CONDITION 1 @type_errno = MYSQL_ERRNO;
  SET @type_errno = 0;
  ALTER TABLE type_rebuild MODIFY id BIGINT NOT NULL, ALGORITHM=INPLACE;
END//
DELIMITER ;
CALL try_type_inplace();
SELECT @type_errno AS inplace_error, COLUMN_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'type_rebuild'
  AND COLUMN_NAME = 'id';
ALTER TABLE type_rebuild MODIFY id BIGINT NOT NULL,
  ALGORITHM=COPY, LOCK=SHARED;
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'type_rebuild'
ORDER BY ORDINAL_POSITION;
SELECT INDEX_NAME, SEQ_IN_INDEX, COLUMN_NAME
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'type_rebuild'
ORDER BY INDEX_NAME, SEQ_IN_INDEX;
SELECT id, code FROM type_rebuild ORDER BY id;
DROP PROCEDURE try_type_inplace;
DROP TABLE type_rebuild;

실행 결과(MySQL 8.0.x):

준비·정리 문장과 일부 긴 SQL 표기를 줄인 발췌다. 조회 결과의 행과 열은 그대로 유지했다.

mysql> CREATE TABLE type_rebuild (...);
Query OK, 0 rows affected

mysql> INSERT INTO type_rebuild VALUES (1,'A'),(2,'B');
Query OK, 2 rows affected
Records: 2  Duplicates: 0  Warnings: 0

mysql> SELECT @type_errno AS inplace_error, COLUMN_TYPE ...;
+---------------+-------------+
| inplace_error | COLUMN_TYPE |
+---------------+-------------+
|          1846 | int         |
+---------------+-------------+
1 row in set

mysql> ALTER TABLE type_rebuild MODIFY id BIGINT NOT NULL,
    ->   ALGORITHM=COPY, LOCK=SHARED;
Query OK, 2 rows affected
Records: 2  Duplicates: 0  Warnings: 0

mysql> SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE ...;
+-------------+-------------+-------------+
| COLUMN_NAME | COLUMN_TYPE | IS_NULLABLE |
+-------------+-------------+-------------+
| id          | bigint      | NO          |
| code        | varchar(12) | NO          |
+-------------+-------------+-------------+
2 rows in set

mysql> SELECT INDEX_NAME, SEQ_IN_INDEX, COLUMN_NAME ...;
+------------+--------------+-------------+
| INDEX_NAME | SEQ_IN_INDEX | COLUMN_NAME |
+------------+--------------+-------------+
| idx_code   |            1 | code        |
| PRIMARY    |            1 | id          |
+------------+--------------+-------------+
2 rows in set

mysql> SELECT id, code FROM type_rebuild ORDER BY id;
+----+------+
| id | code |
+----+------+
|  1 | A    |
|  2 | B    |
+----+------+
2 rows in set

InnoDB의 클러스터형 인덱스는 기본 키 순서로 행을 저장하고 보조 인덱스는 행을 찾기 위한 기본 키 값을 포함한다. 따라서 기본 키 타입의 확대는 해당 컬럼 하나만의 문제가 아니다. 보조 인덱스 레코드 폭, 페이지당 레코드 수, 버퍼 풀에 들어가는 유효 데이터량까지 달라질 수 있다. COPY는 새 테이블 구조에 행과 인덱스를 다시 구성하므로 기존 인덱스 이름이 남아 있다는 사실이 물리적 재구축을 하지 않았다는 뜻은 아니다. 이 예제의 작은 데이터로 실제 페이지 증가율이나 처리 시간을 추정하지 않는다.

LOCK=SHARED는 복사 중 읽기를 허용하는 지정이지, 전체 과정에 잠금 대기가 없다는 뜻이 아니다. 시작·종료 시의 metadata lock, 장기 트랜잭션, 대기 중인 DDL 뒤로 쌓이는 접근 요청까지 관측해야 한다.

컬럼 정의를 다시 지정할 때의 함정

MODIFY에 타입만 쓰면 원래 속성이 모두 자동으로 유지되는 것은 아니다. UNSIGNED, NOT NULL, DEFAULT, COMMENT 등 필요한 컬럼 속성을 완전한 새 정의로 검토한다. 외래 키 부모·자식 컬럼의 타입과 부호, 생성 컬럼, 뷰, 저장 프로그램, 파티션 식도 의존 관계로 확인한다.

5. VARCHAR 확장도 최대 바이트 길이 경계를 넘으면 달라진다

InnoDB가 VARCHAR 길이를 저장하는 데 필요한 길이 바이트 수는 최대 바이트 길이에 영향을 받는다. 최대 길이가 255바이트 이하인 영역과 256바이트 이상인 영역을 넘나드는 변경은 같은 영역 안의 확장과 다르다. 문자셋이 utf8mb4라면 선언 길이만 보고 이 경계를 판단해서는 안 된다. 실제 행이 ASCII만 포함하더라도 컬럼의 가능한 최대 바이트 길이가 중요하다.

다음은 경계를 단순하게 보이기 위해 ascii를 명시한 예제다. VARCHAR(100)에서 VARCHAR(200)으로 늘리는 작업은 INPLACE로 실행하고, VARCHAR(300)으로 늘리려는 작업은 같은 방식에서 거부되는지 확인한다. 따라서 “길이만 늘리므로 항상 온라인으로 가능하다”는 일반화는 성립하지 않는다.

CREATE TABLE type_varchar (
  id INT PRIMARY KEY,
  code VARCHAR(100) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  KEY idx_code (code)
) ENGINE=InnoDB;
INSERT INTO type_varchar VALUES (1,'ABC');
ALTER TABLE type_varchar
  MODIFY code VARCHAR(200) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  ALGORITHM=INPLACE, LOCK=NONE;
DELIMITER //
CREATE PROCEDURE try_varchar_boundary()
BEGIN
  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
    GET DIAGNOSTICS CONDITION 1 @varchar_errno = MYSQL_ERRNO;
  SET @varchar_errno = 0;
  ALTER TABLE type_varchar
    MODIFY code VARCHAR(300) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
    ALGORITHM=INPLACE, LOCK=NONE;
END//
DELIMITER ;
CALL try_varchar_boundary();
SELECT @varchar_errno AS boundary_error,
       CHARACTER_MAXIMUM_LENGTH AS max_chars,
       CHARACTER_OCTET_LENGTH AS max_bytes
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'type_varchar'
  AND COLUMN_NAME = 'code';
SELECT id, code FROM type_varchar;
DROP PROCEDURE try_varchar_boundary;
DROP TABLE type_varchar;

실행 결과(MySQL 8.0.x):

준비·정리 문장과 일부 긴 SQL 표기를 줄인 발췌다. 조회 결과의 행과 열은 그대로 유지했다.

mysql> CREATE TABLE type_varchar (...);
Query OK, 0 rows affected

mysql> INSERT INTO type_varchar VALUES (1,'ABC');
Query OK, 1 row affected

mysql> ALTER TABLE type_varchar
    ->   MODIFY code VARCHAR(200) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
    ->   ALGORITHM=INPLACE, LOCK=NONE;
Query OK, 0 rows affected
Records: 0  Duplicates: 0  Warnings: 0

mysql> SELECT @varchar_errno AS boundary_error ...;
+----------------+-----------+-----------+
| boundary_error | max_chars | max_bytes |
+----------------+-----------+-----------+
|           1846 |       200 |       200 |
+----------------+-----------+-----------+
1 row in set

mysql> SELECT id, code FROM type_varchar;
+----+------+
| id | code |
+----+------+
|  1 | ABC  |
+----+------+
1 row in set

VARCHAR 확장이 지원되더라도 인덱스 키 길이, 행 크기, 복합키 구성 등 다른 한도를 넘지 않아야 한다. INPLACE 성공은 INSTANT 성공과 동의어도 아니다. 테스트 결과는 해당 정의와 버전의 증거로 남기고, 다른 문자셋·인덱스·행 형식으로 자동 일반화하지 않는다.

6. 대용량 운영과 Aurora에서 달라지는 판단

변경 전에 확보할 실행 예산

COPY는 새 정의로 행을 옮기고 인덱스를 구성한다. 원본과 새 구조가 공존하는 공간, 임시 작업 공간, 동시 업무 부하를 함께 고려한다. 완료 직전에 공간 부족이나 metadata lock 문제가 생기면 이미 수행한 복사 시간까지 손실될 수 있다. “자료형을 넓히기만 한다”는 이유로 사전 복제본 시험을 생략하지 않는다.

비동기 복제에서는 원본 DDL이 빨리 끝났다고 복제본도 준비되었다고 볼 수 없다. 복제본의 DDL 적용, 뒤에 쌓인 트랜잭션, 읽기 트래픽의 영향까지 변경 창에 포함한다. 외부 온라인 스키마 변경 도구를 선택하더라도 새 테이블을 만드는 변환 자체가 사라지는 것은 아니다. 도구의 세션 SQL mode, 변환 경고 처리, 중복 키 처리, 트리거·외래 키 제약을 따로 검증한다.

Aurora는 자동으로 모든 타입 변경을 즉시 처리하지 않는다

Aurora MySQL 3은 Community MySQL 8.0의 instant DDL과 호환되는 기능을 제공하며 AWS는 Aurora MySQL 8.4에도 instant DDL을 설명한다. 이것이 임의의 타입 변경까지 ALGORITHM=INSTANT로 가능하다는 뜻은 아니다. Aurora MySQL 2의 Fast DDL과 후속 버전의 instant DDL을 같은 기능으로 취급하지 않는다.

실제 클러스터에서는 정확한 엔진 버전에서 해당 문장의 지원 여부를 확인하고 writer의 부하, reader의 지연, 클러스터 저장 공간과 인스턴스 로컬 임시 공간을 구분해 점검한다. 일반 MySQL 비동기 복제의 DDL 비용 모델을 Aurora 내부 reader에 그대로 대입하지 않는다. 이 글의 로컬 결과는 Aurora에서의 알고리즘·잠금·소요 시간을 입증하지 않는다.

7. 변경과 되돌리기의 완료 조건

스키마를 이전 타입으로 되돌리는 DDL은 데이터 복구와 다르다. 문자열이 이미 잘렸거나 소수부가 바뀌었으면 컬럼 폭을 다시 늘려도 원래 값이 돌아오지 않는다. 복구를 요구하는 변경에는 검증된 백업, 원본 값 보관, 또는 별도 컬럼을 통한 병행 전환이 필요하다.

중단 기준도 착수 전에 정한다. 예상하지 못한 경고, 변환 후 키 충돌, 허용치를 넘는 잠금 대기·복제 지연, 저장 공간 여유 감소가 나타나면 다음 단계로 진행하지 않는다. 실행 중 DDL 취소와 완료된 DDL의 역방향 변경은 서로 다른 작업이며, 어느 쪽도 즉시 끝난다고 가정하지 않는다.

  • 원래의 NOT NULL, DEFAULT, UNSIGNED
  • 실제 엔진 버전에서 명시한 ALGORITHM과 LOCK

정리

자료형 변경은 “새 컬럼 정의가 허용되는가”보다 넓은 문제다. 값의 의미가 유지되는지, 질의의 비교 규칙이 맞는지, 선택한 알고리즘의 운영 비용을 감당할 수 있는지를 각각 증명해야 한다. strict mode와 명시적 알고리즘은 중요한 안전장치지만, 전수 데이터 검증과 애플리케이션 호환성 검증을 대신하지 않는다. 이 구분은 이후 문자셋·collation 변경이나 온라인 스키마 전환을 설계할 때도 같은 기준이 된다.

참고 문서