![[MySQL] 문자열 데이터를 저장하는 방법](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCpc8g%2FbtsMUy3DgCA%2FHLvYwfBVeJ8kPHJxtlkJW0%2Fimg.png)

개발을 하다 보면 가장 많이 다루게 되는 데이터는 역시 문자열 데이터입니다.
백엔드 개발자로서 저는 평소 "문자열 데이터를 어떻게 하면 효율적으로 저장하고 불러올 수 있을까?"라는 고민을 가장 많이 하는 것 같습니다.
특히 데이터 저장을 위해서는 반드시 테이블이 필요한데요. 문자열 데이터를 저장하기 위한 테이블을 설계하다 보면 자연스럽게 한 가지 고민에 부딪히게 됩니다.
바로, 문자열 데이터를 저장할 때 어떤 컬럼 타입을 선택해야 할지에 대한 고민이죠. (CHAR와 VARCHAR, 과연 무엇을 선택해야 할까요?)
CHAR VS VARCHAR
CHAR
타입과 VARCHAR
타입은 모두 문자열을 저장하기 위해 고안된 컬럼 타입이에요.
두 타입 모두 다양한 문자 인코딩 방식(ASCII, UTF-8 등)을 지원하며, 컬럼을 정의할 때 최대 길이를 지정하는 방법 또한 유사합니다.
그렇다면 두 타입의 차이점은 무엇일까요?
바로 디스크 공간을 사용하는 방식에 있어요.
두 타입의 디스크 공간을 사용하는 방식
CHAR와 VARCHAR의 핵심적인 차이는 데이터를 고정된 길이로 저장하느냐(고정 길이), 아니면 실제 데이터 크기에 맞춰 유연하게 저장하느냐(가변 길이)에 있습니다.
먼저 CHAR 타입은 항상 선언된 길이 만큼의 공간만을 사용합니다.
예를 들어, 타입이 CHAR(10)
으로 선언된 컬럼에 'abc'라는 문자가 저장되면 실제로 'abc'에 해당하는 값만 저장되는 것이 아니라 나머지 빈 공간까지 모두 할당됩니다
즉, 실제 문자열이 3글자임에도 불구하고, 10글자에 해당하는 공간이 모두 사용되는 것이죠.
반면, VARCHAR 타입은 실제 데이터의 길이에 맞춰 가변적으로 공간을 할당합니다.
즉, 실제 필요한 공간만을 사용하기 때문에 공간사용성이 CHAR 타입에 비해 훨씬 우수하다는 것을 알 수 있죠.
다만, 저장된 데이터의 길이가 얼마나 되는지를 추가로 저장해야해요. 이를 길이 정보 저장 오버헤드
라고 해요.
VARCHAR 타입 컬럼의 크기가 255바이트 미만인 데이터에 대해서는 추가로 1바이트 크기의 공간을 확보해서 해당 컬럼의 길이가 얼마인지를 별도로 저장해 둬야 합니다. 그 이상의 크기에는 2바이트 크기 공간에 길이를 저장하죠.
예를 들어, VARCHAR(10)으로 선언된 컬럼에 'abc'라는 문자가 저장되면 'abc'(3byte) 에, 길이 정보 저장 오버헤드(1byte)가 함께 저장되어 4byte정도의 공간을 차지하게 되는거죠.
이렇게 비교해보면 CHAR 타입은 디스크 공간을 예측 가능하고 일정하게 사용할 수 있고, 그 대신 VARCHAR 타입은 디스크 공간을 조금 더 효율적으로 사용하도록 동작한다는 걸 알 수 있었어요.
그래서 여기까지만 보고 생각한다면 다음과 같이 생각할 것 같아요
아! 문자열의 길이가 항상 일정하면 CHAR, 가변적이라면 VARCHAR을 고려하면 되겠구나!
하지만, 이건 큰 오해였어요.
문자열 데이터의 크기가 변경되는 경우를 고려하자

예를 들어, 카카오톡 메시지를 저장하는 컬럼이 VARCHAR 타입으로 설정되어 있고, 여기 "안녕"이라는 문자열을 저장한다고 가정해 봅시다.
VARCHAR는 실제 저장할 데이터의 길이만큼 정확하게 공간을 사용합니다. 따라서 "안녕"이란 데이터를 저장하면, 길이 정보를 포함한 딱 맞는 공간이 디스크에 할당되고 저장됩니다.

메시지를 보내고 보니 갑자기 너무 무뚝뚝한 것 같아 걱정이 됩니다. 그래서 급히 "안녕"을 "안녕하세요"로 수정했습니다.
하지만 기존의 VARCHAR 공간에는 딱 "안녕" 정도만 저장할 수 있는 공간이 확보되어 있었죠. 더 긴 문자열인 "안녕하세요"를 넣기엔 공간이 부족했어요.
즉 길이가 더 큰 값으로 변경될 때에는 레코드 자체를 다른 공간으로 옮기는 로우 마이그레이션
현상이 발생합니다.
이 레코드가 이동하는 비용이 길이 공간을 몇바이트 못쓰는 것보다 더 큰 비용으로 다가올 때가 있는 것이죠.
즉 데이터 길이 변화가 적고 업데이트가 빈번하다면 CHAR을 고려하는 것이 좋습니다.
스키마가 변경되는 경우를 고려하자
MySQL은 데이터가 변경되는 도중에도 스키마 변경이 되는 Online DDL
기능을 제공합니다.
서비스가 운영되는 중에도 유연하게 스키마를 바꿀 수 있다는 장점이 있죠.
예를 들어 VARCHAR(10)으로 선언된 컬럼을 VARCHAR(60)으로 변경을 시도한다고 가정해볼게요.
ALTER TABLE test
MODIFY value VARCHAR(60),
ALGORITHM = INPLACE,
LOCK = NONE;
결과는 다음과 같아요
Query OK, 0 rows affected (0.00 sec)
이와 같이 인플레이스 알고리즘(테이블 복사 없이 내부 테이블 만으로 동작하는 알고리즘)을 사용하여 별도의 락 없이 빠르게 스키마 변경이 가능했습니다.
그런데 60자라는 길이가 너무 애매해서 64자로 늘려볼게요.
ALTER TABLE test
MODIFY value VARCHAR(64),
ALGORITHM = INPLACE,
LOCK = NONE;
하지만 다음과 같은 에러가 발생했어요.
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE.
Try ALGORITHM=COPY
왜 이렇게 동작했을까요?
VARCHAR(60)은 utf8mb4 문자셋을 사용할 때 최대 240바이트(60글자 × 4바이트)로 저장됩니다. 이때는 길이 정보를 저장하는 공간이 1바이트면 충분하죠.
그런데 VARCHAR(64)는 최대 256바이트(64글자 × 4바이트)를 사용하게 되어, 길이 정보 저장에 필요한 공간이 1바이트에서 2바이트로 늘어납니다. 이렇게 문자열 길이 정보를 저장하는 공간 자체가 바뀌는 경우엔, 내부적으로 단순한 스키마 변경이 아닌 테이블 전체 복사가 필요하게 됩니다.
그래서 아래와 같이 수정해볼 수 있어요.
ALTER TABLE test
MODIFY value VARCHAR(64),
ALGORITHM = COPY,
LOCK = SHARED;
이때는 테이블 복사 작업이 발생하고 공유 락(SHARED LOCK)이 걸려 서비스 성능에 큰 영향을 미칠 수도 있겠죠.
스키마 변경의 경우 위의 경우 처럼 흔한 일은 아니지만, 이것도 고려해보면 좋은 요소지요!
결국, CHAR 타입의 공간 비효율 문제와 VARCHAR 타입의 데이터 변경 및 스키마 변경 비용 문제를 모두 고려하여, 자신의 서비스 특성과 데이터 변화 패턴에 맞는 타입을 선택하는 것이 가장 중요합니다.
vs TEXT
TEXT 타입은 대용량 텍스트 데이터를 저장하기 위해 특화된 타입입니다. 따라서 CHAR나 VARCHAR와는 다르게, 별도의 최대 길이를 선언하지 않습니다.
TEXT 타입이 이렇게 동작할 수 있는 이유는 데이터를 저장하는 방식 때문입니다. TEXT 타입 데이터는 일반적으로 테이블의 데이터 페이지 내부가 아닌 외부 공간에 저장됩니다.
이를 Off-Page 방식이라 하는데, 테이블 레코드 내에는 실제 데이터가 저장된 외부 공간의 위치를 가리키는 포인터만 저장됩니다.
따라서 TEXT 데이터를 사용할 때마다 외부 공간을 별도로 조회하는 추가 비용이 발생합니다.
즉, 사용할 때 주의가 필요하고 남용해서는 안됩니다. 일반적으로 데이터 크기가 매우 크고 예측이 어렵거나 제한이 없는 경우에 TEXT를 사용하는 것이 좋습니다.
무조건 길이가 길다고 TEXT를 선택할 필요는 없어요. 레코드의 전체 크기가 64KB만 넘지 않는다면 그 안에서는 VARCHAR나 CHAR을 고려해도 좋아요.
'Computer Science > Data 📊' 카테고리의 다른 글
DB의 트랜잭션 (0) | 2024.05.16 |
---|---|
데이터베이스 vs 데이터 웨어하우스 vs 데이터레이크 (0) | 2024.03.08 |
안녕하세요, 저는 주니어 개발자 박석희 입니다. 언제든 하단 연락처로 연락주세요 😆