MySQL JSON vs. TEXT

JSON 타입 컬럼으로 저장하는 것이 좋을까요 ?
TEXT타입 컬럼이 좋을까요 ?

MySQL 서버도 JSON 타입 컬럼을 지원하기 시작하면서, 사용자에게는 편의성과 생산성 측면에서 더 많은 선택지가 생겼어요.

그런데 사실 이는 새로운 고민의 시작이기도 해요. 특히 JSON 데이터를 저장할때, JSON 타입을 사용하는 것이 좋을지 아니면 TEXT 타입 컬럼이 좋을지 고민하는 경우가 많이 있을 것 같아요. 오늘은 JSON vs. TEXT 판단 기준을 위한 가이드라인을 공유해 보려고 해요.

JSON

성능 비교 (대용량 컬럼)

각 컬럼 타입별로 지원하는 기능과 성능은 컬럼 타입 선정에서 매우 중요한 판단 기준이에요. 그래서 먼저 JSON 컬럼 타입과 TEXT 컬럼 타입의 성능을 먼저 살펴보려고 해요.

성능 테스트를 위해서, 아래와 같이 간단한 테이블을 만들고 16000건 정도의 레코드를 준비했어요. 두 테이블의 data 컬럼은 타입만 다르고, 저장된 값은 똑같이 준비했어요. 두 테이블 모두 data컬럼의 값이 매우 커서, 테이블의 레코드에 같이 저장되지 못하고, External page (off-page)로 저장된다는 것을 기억해주세요.

CREATE TABLE huge_json (
id INT AUTO_INCREMENT PRIMARY KEY,
data JSON
);
CREATE TABLE huge_text (
id INT AUTO_INCREMENT PRIMARY KEY,
data MEDIUMTEXT
);
-- // 테스트 데이터 생성
-- // 4000여개 필드를 가진 100KB 정도의 Json 값을 data 컬럼에 저장
SELECT AVG(LENGTH(data)) FROM huge_json; -- // 107352 바이트
SELECT AVG(LENGTH(data)) FROM huge_text; -- // 107352 바이트
SELECT COUNT(*) FROM huge_json; -- // 16000 건
SELECT COUNT(*) FROM huge_text; -- // 16000 건

준비된 2개 테이블에 대해서, 아래와 같은 몇 가지 테스트 결과를 살펴보고자 해요.

1) SELECT COUNT(id)               FROM huge_[ json | text ];
2) SELECT COUNT(data) FROM huge_[ json | text ];
3) SELECT COUNT(data->>'$.field') FROM huge_json;
4) SELECT id FROM huge_[ json | text ];
5) SELECT data FROM huge_[ json | text ];

위의 테스트 쿼리들은 MySQL 서버에서 다음과 같은 처리들을 수행해요.

  • SELECT COUNT(id) MySQL 서버는 이 쿼리를 처리하는 도중, 테이블의 JSON 이나 TEXT 컬럼을 전혀 접근하지 않아요. (즉 External page 를 전혀 접근하지 않아요)
  • SELECT COUNT(data) MySQL 서버는 쿼리를 처리하기 위해서, data 컬럼의 값을 꼭 읽어야 해요. (즉, External page 를 접근해야 해요. 하지만 MySQL 서버는 JSON 컬럼의 경우에도, Json parsing작업을 필요로 하진 않아요.)
  • SELECT COUNT(data->>'$.field') MySQL 서버는 쿼리를 처리하기 위해서, data 컬럼의 값을 읽고 Json parsing 작업을 필요로 해요.

이런 특성 덕분에 쿼리 처리의 각 단계별 성능 비교 테스트에 적합해 테스트 쿼리로 선정했어요.

테스트 결과, SELECT COUNT(...) 쿼리들은 다음과 같은 성능을 보여주었어요. 이 그래프에 표시된 시간은 쿼리 실행 시점부터 모든 결과 데이터를 전송 받는 시점까지를 밀리초 단위로 수집한 것이에요. 그리고 그래프의 Y 축은 Log 눈금이어서, 눈금 간격간 값의 변화는 매우 크다는 것을 기억해주세요.

예상했던 대로, JSON 컬럼의 값을 전혀 접근하지 않는 경우(1번 쿼리)와 JSON 컬럼의 값을 읽더라도 Parsing 하지 않는 경우(2번 쿼리)에는 TEXT 타입과 JSON 타입을 사용하는 경우 거의 비슷한 성능을 보여주었어요. (TEXT 타입과 JSON 타입간 미세한 차이를 보이긴 하지만, 오늘 이야기에서는 동일한 결과로 간주해요.)

그런데 JSON Parsing 작업이 필요한 SELECT COUNT($->>'$.field') 쿼리(3번 쿼리)는 SELECT COUNT(data) 쿼리보다 조금 더 시간이 걸린 것을 확인할 수 있어요. 아마도 이 시간이 16,000건 JSON 데이터를 Parsing 하는데 걸린 시간이라고 볼 수 있을 것 같아요. 예상보다 JSON 데이터를 분석하는데 시간이 많이 걸리진 않아요. 참고로, MySQL 서버의 JSON Parsing 과정은 부분적으로 실행되지 않고, 항상 Full Parsing을 실행해요. 즉, JSON 값의 제일 첫번째 필드를 가져오는 작업과 마지막 필드를 가져오는 데 걸리는 시간은 크지 않다는 의미예요.

이제 JSONTEXT 컬럼의 값을 클라이언트로 가져오는 쿼리의 성능 테스트를 살펴 볼게요. 이 그래프에 표시된 시간은 쿼리 실행 시점부터 모든 결과 데이터를 전송 받는 시점까지(클라이언트에서 쿼리 결과를 처리하는데 걸린 시간은 포함되지 않음)를 수집한 것이에요. 그리고 그래프의 Y 축은 Log 눈금이어서, 눈금 간격간 값의 변화는 매우 크다는 것을 기억해주세요.

SELECT id 쿼리(4번 쿼리)는 TEXTJSON 컬럼의 값을 가져오지 않기 때문에 거의 동일한 시간이 걸린 것을 알 수 있어요. 하지만 TEXTJSON 컬럼의 값을 가져오는 테스트(5번 쿼리)에서는 JSON 컬럼이 TEXT 컬럼보다 2.5배 이상 많은 시간이 소요된 것을 확인할 수 있어요.

SELECT COUNT(data) 쿼리와 달리 SELECT data 쿼리의 경우, TEXT 컬럼과 JSON 컬럼의 값을 읽고 처리후 클라이언트로 전송해야 해요. TEXT 컬럼의 경우 문자열로 해석하는 작업이 필요하고, JSON 컬럼의 경우 MySQL 서버 내부적인 Binary JSON 저장 포맷으로 변환해야 해요. 이때, TEXT 컬럼 대비 JSON 컬럼의 변환 처리가 훨씬 복잡하기 때문에 4번과 5번 쿼리는 큰 성능 차이를 보이게 된 거예요. SELECT COUNT(data) 쿼리보다 SELECT data 쿼리가 처리 시간이 훨씬 많이 걸린 또다른 이유는 네트워크 전송에 소요된 시간 때문(JSON 데이가 매우 큰 값이어서 16,000건 전송이 상당한 시간이 걸렸어요)이에요.

그런데 SELECT data->>'$.field' 쿼리(3번)와 SELECT data 쿼리(5번) 쿼리는 둘 모두 JSON Parsing 작업이 필요한데, 이 두 쿼리의 성능 차이가 매우 크게 발생하는 것은 이상하죠. 이는 JSON 컬럼의 값을 네트워크로 전송하는 방식 때문이에요. MySQL 서버는 데이터 파일에서 읽은 JSON 데이터를 Binary JSON DOM구조로 관리하는데, 네트워크로 전송하기 위해서는 Binary JSON데이터를 문자열로 변환하면서 Serialization 작업이 필요해요. 이 과정에서 JSON 데이터의 모든 필드별로 타입 변환 과정이 필요하기 때문에, JSON 값의 필드 개수만큼 타입 변환 및 Serialization 작업이 호출되면서 시간이 많이 걸린 거예요. PC에서 진행한 간단한 테스트에 의하면, 100KB JSON 타입 컬럼의 Serialization에는 1 밀리 초 이상 걸렸지만, TEXT 타입의 컬럼을 Serialization에는 1 마이크로 초도 걸리지 않았어요.

성능 비교 (소용량 컬럼)

지금까지는 100KB 정도의 대용량 컬럼에 대해서 성능을 살펴보았는데요. 이번에는 600 바이트 정도의 소용량 (테이블의 구조와 레코드 건수는 동일한 상태에서) TEXT 타입과 JSON 타입 컬럼의 성능을 비교해 보았어요. 절대적인 시간은 많이 줄었지만 여전히 TEXT 타입과 JSON 타입 컬럼의 데이터를 가져오는 쿼리는 큰 시간 차이(비율)를 보였어요.

기능 비교

이제 JSON 타입과 TEXT 타입 컬럼의 기능 차이를 살펴보려고 하는데요. 여기에서 기능 비교 기준은 JSON 타입 컬럼과 TEXT 타입 컬럼에 JSON 데이터를 저장한다고 가정할 때의 차이예요.

JSON 타입 컬럼은 MySQL 서버가 JSON 데이터의 구조(Json DOM)를 인식하고 있다는 게 가장 큰 차이예요. 아래에서 살펴볼 JSON 컬럼과 TEXT 컬럼의 모든 차이는 이로 인한 것들인데, JSON 컬럼은 TEXT 컬럼 대비 아래와 같은 몇가지 장점들을 가지고 있어요.

  • JSON 타입은 MySQL server-side에서 필드의 값을 조회 및 변경 가능
  • JSON 타입은 특정 필드 변경시 in-place 업데이트 가능
  • JSON 타입은 특정 필드에 대해서 인덱스를 생성 가능

TEXT 타입 컬럼에 저장된 1MB JSON 데이터의 일부분만 변경하고자 해도, 1MB를 통째로 업데이트해야 해요. 하지만 JSON 타입 컬럼에 저장된 경우에는, MySQL 서버의 JSON 함수들을 이용해서 특정 필드만 변경할 수 있어요. 뿐만 아니라 MySQL 서버는 변경되는 필드가 고정 길이인 경우, in-place 업데이트를 할 수 있어요. 즉, 1MB 데이터를 모두 디스크에 업데이트하는 것이 아니라 변경된 필드의 값만 디스크에 기록하면 업데이트가 완료되기 때문에 빠르게 처리할 수 있어요.

또한 JSON 컬럼에 저장된 데이터의 특정 필드를 추출해서 인덱스(함수 기반 인덱스)를 생성하고, 빠른 검색에 활용할 수 있어요.

결론적으로

지금까지 살펴봤던 성능과 기능을 종합적으로 판단해보면, 아래와 같은 가이드라인을 만들어 볼 수 있어요.

아래 요건이 필요한 경우에는 JSON 타입 컬럼 사용이 좋아요. 이런 요건들이 필요치 않은 경우라면, TEXT 타입 컬럼을 사용하면 JSON 타입 컬럼보다 빠른 성능 효과를 얻을 수 있어요.

  • JSON 데이터의 특정 필드만 접근이 가능해야 한 경우
  • JSON 데이터의 특정 필드(고정 길이 필드)만 자주 업데이트되는 경우
  • JSON 데이터의 특정 필드로 인덱스 생성이 필요한 경우

마지막으로 하나 더 !

일반적으로 온라인 트랜잭션(OLTP) 처리 용도의 RDBMS에서는, 가볍고 빠른 쿼리들이 매우 빈번하게 처리되는 경우가 대부분이에요. 이런 온라인 트랜잭션 처리용 DBMS 서버에서는 수십 KB이상의 데이터를 한두 개의 컬럼에 저장하고, 이를 빈번하게 접근하는 것은 상당히 큰 부하를 유발해요. 수십 수백 KB 이상의 데이터는, 높은 CPU 사용량과 느린 응답 속도 그리고 네트워크 대역폭 사용량까지 걱정해야 하는 상황이 발생할 수도 있어요. 만약 여러분이 성능에 매우 민감한 DBMS 서버를 사용하고 있다면, DBMS 서버에 저장되는 데이터는 최대한 컴팩트하게 유지하시는 것을 추천해요.

만약 큰 데이터를 꼭 저장해야 한다면, 별도의 DBMS 서버로 분리하거나 DBMS 이외의 다른 저장소를 활용하는 방안도 고려해보실 것을 추천해요.

당근마켓에서 함께 고민을 나누고 싶다면 여기를 눌러 당근마켓 채용 공고를 확인해보세요!

--

--

당근마켓은 동네 이웃 간의 연결을 도와 따뜻하고 활발한 교류가 있는 지역 사회를 꿈꾸고 있어요.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store