테마
문자열 타입과 문자 집합
문자열 칼럼은 두 가지를 정해야 한다. 하나는 공간을 어떻게 잡을지(CHAR냐 VARCHAR냐)이고, 다른 하나는 글자를 어떤 바이트로 저장하고 어떤 규칙으로 비교할지(문자 집합(character set)과 콜레이션(collation))다. 두 번째가 비교 결과와 인덱스 크기를 바꾸고, 길이를 늘리는 ALTER TABLE이 바로 끝날지 테이블을 통째로 복사할지까지 좌우한다.
CHAR와 VARCHAR
CHAR(M)과 VARCHAR(M)의 M은 바이트가 아니라 글자 수다. CHAR는 0~255글자까지 선언할 수 있고, 짧은 값이 들어오면 오른쪽을 공백으로 채워 선언한 길이만큼 자리를 잡는다. 읽을 때는 그 뒤쪽 공백을 떼고 돌려준다. VARCHAR는 값의 실제 길이만큼만 쓰고, 앞에 "이 값이 몇 바이트인지"를 적은 길이 바이트를 붙인다. 값이 255바이트 이하일 수 있는 칼럼은 길이 바이트 1개, 그보다 길어질 수 있으면 2개를 쓴다.
| 넣은 값 | CHAR(4)에 저장 | 크기 | VARCHAR(4)에 저장 | 크기 |
|---|---|---|---|---|
'' | ' ' | 4바이트 | '' | 1바이트 |
'ab' | 'ab ' | 4바이트 | 'ab' | 3바이트 |
'abcd' | 'abcd' | 4바이트 | 'abcd' | 5바이트 |
위 표는 한 글자가 1바이트인 문자 집합 기준이다. 글자마다 바이트 수가 다른 utf8mb4에서는 InnoDB가 CHAR(N)도 최소 N바이트만 예약하고 뒤쪽 공백을 잘라 가변 길이처럼 저장한다.
국가 코드·해시값처럼 길이가 늘 같은 값은 CHAR가 길이 바이트를 아낀다. 이름·이메일처럼 길이가 제각각인 값은 VARCHAR가 맞다. VARCHAR의 최대 길이는 한 행의 모든 칼럼이 나눠 쓰는 65,535바이트 한도에 묶인다. 엄격 모드(strict mode)에서는 선언보다 긴 값을 넣으면 잘라 넣지 않고 오류를 낸다.
문자 집합과 콜레이션
문자 집합은 글자를 어떤 바이트 열로 바꿔 저장할지 정한 규칙이다. MySQL 8.0부터 서버 기본 문자 집합은 utf8mb4이고 8.4도 같다. utf8mb4는 글자 하나를 1~4바이트로 저장하며, 한글은 3바이트, 이모지는 4바이트를 쓴다. 옛 utf8mb3는 글자당 최대 3바이트라 이모지를 담지 못하고, 8.4에서 폐기 예정(deprecated)이다. 이름 utf8은 지금 utf8mb3의 별칭이므로 새 테이블에는 utf8mb4를 명시한다.
콜레이션은 같은 문자 집합 안에서 문자열을 비교·정렬하는 규칙이다. 8.4의 utf8mb4 기본 콜레이션은 utf8mb4_0900_ai_ci다. 이름을 조각내 읽으면 뜻이 보인다.
| 조각 | 뜻 |
|---|---|
0900 | 유니코드 정렬 알고리즘 9.0.0 기준 |
ai / as | 악센트 무시 / 구분(é와 e를 같게 볼지) |
ci / cs | 대소문자 무시 / 구분 |
bin | 글자 코드값 그대로 비교 |
문자 집합과 콜레이션의 기본값은 서버, 데이터베이스, 테이블, 칼럼 순으로 내려가지만 아래 단계를 만들 때의 기본값일 뿐이라, 서버 설정을 바꿔도 이미 만든 테이블은 그대로다.
PAD SPACE와 NO PAD
콜레이션마다 뒤쪽 공백을 비교에 넣을지가 다르다. PAD SPACE(공백 채움) 콜레이션은 비교할 때 짧은 쪽 뒤에 공백을 채워 보므로 'a'와 'a '가 같다. NO PAD(공백 채움 없음) 콜레이션은 공백도 다른 글자와 똑같이 비교하므로 둘이 다르다. utf8mb4_0900_ai_ci처럼 이름에 0900이 든 콜레이션은 NO PAD이고, utf8mb4_general_ci·utf8mb4_unicode_ci·utf8mb4_bin 같은 옛 콜레이션은 PAD SPACE다.
그래서 5.7에서 옮겨 온 옛 콜레이션 테이블과 8.x 기본 테이블은 같은 비교도 결과가 다를 수 있다. 콜레이션은 중복 판단도 바꾼다. 유니크 인덱스(unique index)는 콜레이션 기준으로 같은 값인지 보므로, _ci에서는 'Kim'과 'kim'이 중복이 되어 두 번째 INSERT가 막힌다(유니크 인덱스). 대소문자를 구분할 칼럼은 _cs나 _bin을 따로 지정한다.
콜레이션이나 문자 집합이 다른 칼럼끼리 비교·조인할 때도 문제가 생긴다. 같은 문자 집합에서 utf8mb4_0900_ai_ci와 utf8mb4_general_ci처럼 콜레이션이 다르면 어느 규칙을 쓸지 정할 수 없어 Illegal mix of collations 오류가 난다. 한쪽이 _bin 콜레이션이면 오류 없이 _bin 규칙으로 비교한다. utf8mb3와 utf8mb4처럼 문자 집합이 다르면 오류는 없지만 한쪽 값을 변환해 비교해야 하므로 인덱스를 못 쓸 수 있다. 그래서 조인 칼럼은 문자 집합과 콜레이션을 맞춘다.
VARCHAR 길이 변경과 255바이트 경계
VARCHAR 길이를 늘리는 DDL(Data Definition Language, 데이터 정의어)은 길이 바이트 개수가 그대로일 때만 테이블을 복사하지 않고 제자리(ALGORITHM=INPLACE)에서 끝나며, 그동안 다른 세션의 쓰기도 막지 않는다. 최대 바이트 길이가 255를 넘어가면 길이 바이트가 1개에서 2개로 바뀌어 모든 행을 다시 써야 한다. 이때는 테이블 복사(ALGORITHM=COPY)가 필요하고, 복사하는 동안 쓰기가 막힌다.
경계는 글자 수가 아니라 바이트로 센다. utf8mb4는 글자당 최대 4바이트로 계산하므로 VARCHAR(63)은 252바이트, VARCHAR(64)는 256바이트라 63글자에서 64글자로만 늘려도 경계를 넘는다. 길이를 줄이는 것과 타입 자체를 바꾸는 것(INT를 BIGINT로 등)은 언제나 테이블 복사다.
sql
-- utf8mb4 칼럼 기준
ALTER TABLE users MODIFY nickname VARCHAR(60), ALGORITHM=INPLACE, LOCK=NONE; -- 40→60: 둘 다 255바이트 이하, 제자리 변경
ALTER TABLE users MODIFY nickname VARCHAR(80), ALGORITHM=INPLACE, LOCK=NONE; -- 60→80: 경계를 넘어 오류, COPY 필요ALGORITHM=INPLACE(테이블 복사 금지)와 LOCK=NONE(변경 중 쓰기 차단 금지)을 명시하면 제자리 변경이 불가능할 때 조용히 복사로 넘어가지 않고 바로 오류가 나므로, 운영 중 스키마 변경에서 안전하다. 자랄 여지가 있는 칼럼은 처음부터 경계 위 길이(utf8mb4라면 64글자 이상)로 잡아 두는 것도 방법이다. 다른 스키마 변경의 방식과 잠금은 온라인 DDL에서 다룬다.
면접 포인트
- CHAR vs VARCHAR: 고정 길이 공백 채움 vs 실제 길이 + 길이 바이트(1 또는 2). 길이가 고정인 값만 CHAR.
M은 글자 수이고 바이트는 문자 집합이 정한다(utf8mb4는 글자당 최대 4바이트). - utf8은 utf8mb3의 별칭이라 이모지를 못 담는다. 8.0·8.4 기본은 utf8mb4 +
utf8mb4_0900_ai_ci. 0900콜레이션은 NO PAD라'a' <> 'a '._ci면 대소문자만 다른 값이 유니크 중복.- VARCHAR 확장은 255바이트 경계를 넘지 않을 때만 제자리 변경. utf8mb4는 63→64글자에서 넘는다.
