1. MySQL에서의 문자 데이터 타입 의문점
MySQL와 PostgreSQL에서는 String 데이터 타입으로 VARCHAR와 TEXT가 존재합니다.
전자는 가변길이 문자열로 고정된 길이 없이 자유자재로 길이가 달라지는 문자열의 데이터 타입을 의미합니다. 이와 반대로 고정 길이 문자열인 CHAR은 지정한 길이의 문자열을 튜플에 저장하게 됩니다. MySQL에서는 CHAR를 쓰는 게 VARCHAR 보다 성능이 조금 더 좋다고 하죠? 그 이유는 여러 가지가 있는데 조사한 바론 대표적으로 아래의 이유가 있었습니다.
- CHAR는 고정된 길이의 공간에서 값이 저장되므로, 값의 수정이 발생하는 경우 그 공간을 재활용할 수 있다. 하지만, VARCHAR의 경우 현재 저장된 값보다 긴 문자열로 수정이 될 때, 새로운 영역에 값을 할당해야 하므로 이러한 로직에서 추가적인 지연 시간이 발생한다. -> 벤더마다 달라서 VARCHAR의 블럭 파편화가 발생하지 않게 할 수도 있다고 합니다
사실 체감할 만한 정도의 차이가 크지 않다고 하는데, 그럼에도 지연이 발생하는 것이므로, 위의 이유로 값이 잘 바뀌는 경우 CHAR를 쓰고, 값이 잘 바뀌지 않는 경우에는 VARCHAR를 쓰곤 한다고 하네요.
추가적으로 CHAR에서는 남은 길이에 대하여 공백을 할당하므로 데이터 낭비가 발생하긴 합니다. 그래서 메모리 상으로는 VARCHAR가 더 좋긴 하죠!
후자인 TEXT는 최대 길이를 지정하지 않고 저장하는 문자열 타입입니다. 기본적으로 65535 바이트의 크기까지 저장할 수 있습니다.
VARCHAR 길이의 궁금증
일단 VARCHAR의 길이 제한을 보면서 이런 생각이 들었습니다.
왜 굳이 길이를 제한해야 할까? VARCHAR의 길이는 큰 값으로 지정하면 딱히 길이 제한 신경 안 쓰고 데이터를 저장할 수 있지 않나?
하지만, 이러한 생각은 생각 외로 단순하게 해결됐습니다. 마침 당근 마켓에서 기고한 포스팅을 발견했거든요.
https://medium.com/daangn/varchar-vs-text-230a718a22a1
VARCHAR가 가질 수 있는 길이는 일단 16383 까지입니다.
그럼 16383 까지 써볼까? 하는 생각에 문자열 길이가 16383인 컬럼을 만들고자 했는데,
이렇게 공간을 오버해서 사용하게 됐습니다.
16382까지는 생성이 가능했는데, 이러면 추가적인 컬럼을 추가하고자 하더라도 사용하지 못하는 불상사가 발생합니다.
VARCHAR 말고 TEXT를 쓰면 안 되나?
그래서 TEXT에 대해서 이런 생각이 들기도 했습니다.
그러면 굳이 VARCHAR로 아슬아슬하게 하지 말고 TEXT로 자유롭게 사용하면 어떨까?
실제로 상당히 편하게 테이블을 생성할 수 있었습니다.
하지만, 일반적으로 오랜 시간동안 사람들이 TEXT 말고 웬만해서 VARCHAR를 권고하는 데는 이유가 있다고 생각합니다.
일반적으로 RDBMS에서는 TEXT나 BLOB 같은 LOB(Large Object)는 Off-page라는 외부 공간에 저장합니다. MySQL에서도 튜플의 컬럼 값을 Inline으로 B Tree에 저장하지만, 일정 크기를 벗어나게 되면 External off-page storage에 저장합니다.
Record 버퍼에서의 문제점
흠... TEXT도 그렇고 VARCHAR도 그렇고 사실상 튜플의 크기가 매우 커지면 둘 모두 문제가 되는데, 명확한 차이가 무엇일까요?
먼저 VARCHAR는 가변길이 문자열입니다. 즉, 어느 정도 길이가 변하지만서도 길이를 제한할 수도 있다는 것이죠. MySQL 엔진과 InnoDB 스토리지 엔진은 uchar* records[2] 메모리 포인터를 이용해서 레코드 데이터를 주고 받는데, 이때 실제 레코드 크기에 상관 없이 최대 크기로 할당해 둡니다. VARCHAR의 경우에는 길이가 제한이 되어 있기 때문에 미리 메모리를 할당하여 사용할 수 있게 됩니다.
반면 TEXT와 같은 LOB는 그 크기가 매우 크기 때문에 미리 메모리 공간을 할당할 수 없게 되고, 매번 LOB 연산을 사용할 때면 필요한 만큼 메모리를 할당해야 합니다.
그렇지만, VARCHAR를 사용한다고 하더라도 Off-page로 넘어가면 메모리 버퍼를 사용할 수 없기 때문에 조심해야 합니다.
이처럼 MySQL의 경우에는 둘 간의 메모리 버퍼를 사용하는 방식으로 인해서 TEXT보다는 VARCHAR를 사용하는 것이 더 좋다는 것이 대략적으로 납득이 됐습니다. 하지만, PostgreSQL은 또 다른 문제였습니다.
2. PostgreSQL에서도 둘 간의 차이가 발생하는가?
결론적으로 조사한 결과 CHAR, VARCHAR, TEXT간의 성능 차이는 없었습니다. 즉, 모두 비슷한 성능을 보여주며, CHAR은 추가 비용이 존재할다는 점에서 가장 느렸습니다.
TEXT와 같은 큰 크기의 데이터를 저장하는 데 효과적인 이유는 PostgreSQL의 TOAST 저장소 때문이었습니다.
Toast 저장소란?
Toast 저장소는 The Oversized-Attribute Storage Technique의 약자로 매우 큰 사이즈의 속성값을 저장하는 기술로, 흔히 토스트 저장소라고 부르는데요.
PostgreSQL의 페이지 사이즈는 대부분 8kb 사이즈이며, 이 고정 사이즈를 넘어 가면 압축을 수행합니다. 주테이블과 TOAST 테이블은 분리되어 있으므로 TOAST 테이블 연관 속성을 조회하지 않는 경우에는 성능 상의 차이가 없었습니다. 압축 후에는 TOAST 스토리지에 값을 저장하고, 저장된 포인터를 가리키게 됩니다. 그리고 최대한으로 저장할 수 있는 물리적인 크기는 1GB 정도라고 합니다.
상당히 자료 찾기가 어렵고 이해하기도 어려웠지만 이 정도로 정리할 수 있었습니다.
즉, PostgreSQL에서는 차이가 없으므로 차라리 TEXT를 쓰는 것이 더 최적화된 방법이었습니다.
사실 그간 VARCHAR를 쓰라는 의견을 들어와서 적잖은 충격을 받았습니다.
하지만, 데이터베이스를 하나만 고정해서 쓰는 것이 아닌 다른 사람들과 함께 여러 데이터베이스를 사용하므로, "PostgreSQL에서는 상관 없다더라!" 라고 접근하기 보다는 다른 RDB를 함께 고려하여 일단 VARCHAR를 쓰는 것도 좋은 방법이지 않을까? 하는 생각입니다!