MySQL CHAR와 VARCHAR 타입 비교

신규 프로젝트에서 엔티티 설계와 DB(데이터베이스) 스키마 설계를 하는 과정에서 Enum 타입에 대해 DB 필드 타입 매핑에 궁금증이 생겼다. DB 필드 타입에서 Enum이 존재하지만 사용하지 않고, 문자열 타입으로 관리하기로 했다. (DB는 MySQL 8.0을 사용한다)

문자열 타입으로 CHAR와 VARCHAR가 존재하는데, 특징은 무엇이 있고 타입 결정은 어떤 기준으로 생각해야 하는지 알아보겠다.

CHAR와 VARCHAR는 정의할 때 CHAR(10), VARCHAR(10) 형태로 최대 문자 수의 크기를 선언한다. 선언된 문자 수 크기에 맞춰 문자열을 저장하는 방식에서 가장 큰 차이점이 있다. (본 글에서는 언어에 상관없이 1개의 문자가 차지하는 공간을 1칸이라고 표현했다. 1칸의 공간은 영어인 경우 1바이트, 한글인 경우 2바이트의 크기를 사용한다.)

CHAR 타입은 선언된 최대 문자 수의 크기만큼 문자열을 저장할 수 있으며, 저장 공간 크기를 고정적으로 할당한다. CHAR(10)으로 정의한 필드에 'ABCD' 문자열을 저장한다면 4칸을 사용하고 6칸은 공백으로 넣어 10칸의 물리적 공간을 할당한다. (공백으로 넣은 값은 내부적인 동작이므로 실제 값이 보이거나, 가져올 때는 저장한 문자열만 가져온다)

타입 1 2 3 4 5 6 7 8 9 10
CHAR(10) A B C D 공백 공백 공백 공백 공백 공백

VARCHAR 타입은 선언된 최대 문자 수의 크기만큼 문자열을 저장할 수 있으며, 저장 공간 크기를 가변적으로 할당한다. VARCHAR(10)으로 정의한 필드에 'ABCD' 문자열을 저장한다면 4칸을 사용하며, 4칸의 물리적 공간을 할당한다. (CHAR 타입은 선언한 최대 문자 수 크기만큼 공간을 고정적으로 전부 할당하지만, VARCHAR 타입은 저장하는 문자열 길이만큼 가변적으로 공간을 할당한다)

VARCHAR 타입에서는 선언한 최대 문자 수의 크기보다 1칸의 공간을 더 할당하여 문자열의 길이를 저장한다. 즉, 'ABCD' 문자열을 저장하면 문자열의 길이 4를 저장하는 1칸, 문자열을 저장하는 4칸을 사용하여 최종적으로 5칸의 물리적 공간을 할당한다.

타입 1 2 3 4 5
VARCHAR(10) 4 A B C D

위 내용을 보면 CHAR 타입보다 VARCHAR 타입이 입력한 문자열 만큼만 공간을 사용하니깐 더 효율적이라고 생각할 수 있지만 꼭 그렇지는 않다. 일단 2개의 타입의 최대 문자 수 크기를 10으로 선언하고, 문자열 길이가 10인 'ABCDEFGHIJ'를 입력한다고 생각해 보면 CHAR 타입의 경우 10칸의 저장 공간을 할당하는 방면 VARCHAR 타입은 문자열 길이를 저장하는 공간까지 포함하여 총 11칸의 저장 공간을 할당한다. 해당 내용에서 알 수 있는 부분은 고정된 크기의 문자열만을 입력한다면 CHAR 타입이 효율적이다.

입력한 문자열 CHAR(10) 필요한 저장 공간 VARCHAR(10) 필요한 저장 공간
ABCDE 'ABCDE_____' 10 바이트 'ABCDE' 6 바이트
ABCDEFGHIJ 'ABCDEFGHIJ' 10 바이트 'ABCDEFGHIJ' 11 바이트

그러면 고정된 크기의 문자열이 아닌 경우에는 VARCHAR 타입이 효율적일까? 내부적인 동작 방식을 생각해 봐야 한다.

VARCHAR 타입은 저장된 문자열보다 더 긴 길이의 문자열로 수정하는 경우, 물리적인 레코드 이동 또는 분리가 발생한다고 한다. 즉, VARCHAR(10)을 선언하여 'ABCDE' 문자열을 입력하는 경우 6칸의 저장 공간을 할당했지만, 'ABCDEFGHIJ' 문자열로 수정하면 기존 6칸 크기의 저장 공간에 'ABCDEFGHIJ' 문자열을 저장할 수 없기 때문에 11칸의 저장 공간이 새롭게 할당된다고 생각해 볼 수 있다. (앞서 설명했듯, VARCHAR는 입력하는 문자열의 길이를 저장하기 위해서 내부적으로 1칸의 저장 공간을 추가로 할당한다)

해당 내용에서 알 수 있는 부분은 저장된 문자열 보다 더 긴 길이의 문자열로 수정되는 경우가 많다면, VARCHAR 타입은 비효율적일 수 있다.

그렇다면 CHAR와 VARCHAR 타입은 어떤 기준으로 선택하면 될까? 위에서 이해한 각 타입의 특성을 기반으로 생각해 보자.

  • CHAR 타입
    • 생년월일(YYMMDD), 성별(M / W), 주소 타입(R /J)처럼 문자 길이가 고정되어 있는 경우
    • 배송 상태, 상품 품절/노출 상태처럼 최대 문자 수의 크기가 정해져 있으며, 값이 자주 변경되는 경우
  • VARCHAR 타입
    • 메모, 리뷰처럼 최대 길이가 정해져 있지만, 신규로 저장되는 값의 길이가 일정하지 않은 경우
    • 주소, 배송 요청사항처럼 값이 변경되는 경우가 잦지 않은 경우

이해를 돕기위해 예시로 다시 한번 생각해 보자. 배송 상태에 대한 Enum 타입은 Pending, Progress, Completed 값이 존재하고, CHAR(10), VARCHAR(10)으로 선언했다고 가정한다. 배송 상태에 따라 순차적으로 Pending -> Progress -> Completed 값을 변경하는 경우 CHAR의 경우에는 3바이트, 2바이트, 1바이트의 공간을 낭비한다. (CHAR 타입은 선언된 최대 문자 수 크기만큼 공간을 고정적으로 할당한다) 하지만 VARCHAR의 경우에는 상태 값이 변경될 때마다 문자열의 크기가 커짐에 따라 물리적인 레코드 이동 또는 분리가 발생한다. 결국 CHAR 타입에서 낭비되는 1 ~ 3바이트의 공간보다 VARCHAR에서 발생하는 레코드 이동 또는 분리는 더욱 큰 자원을 낭비한다고 볼 수 있다.

지금까지 설명한 CHAR와 VARCHAR 특징을 참고하여 적절한 데이터 타입을 결정하여 정의하도록 하자.
이상으로 CHAR와 VARCHAR 특징에 대한 포스트를 끝내도록 하겠다.