JSON 타입 정리
MySQL의 JSON 타입에 대하여 정리합니다.
JSON 타입 정리
1. JSON 타입의 등장
- 기존 서비스의 규모가 커지면서 요구사항이 바뀌게 되거나 비즈니스 로직의 변경이 잦아졌다. ->
ALTER를 통한 테이블 구조 변경 작업 -> 테이블 구조 변경시 수분~수십분 정도의 테이블 락이 발생하게 되었다. - 결국, 자주 바뀌는 부분은 JSON으로 값을 넣도록 변경되었다.
2. MySQL 지원 현황
- 5.7 부터 JSON 타입 공식 지원한다.
- 내부적으로 바이너리 타입으로 사용한다.
- 여러 JSON 함수를 지원한다. (
JSON_EXTRACT(),JSON_SET())
3. JSON 타입의 장점
1) 스키마 유연성
ALTER를 통하여 테이블 구조를 수정하지 않고 여러 데이터를 하나의 필드에 추가할 수 있다.- 정규화를 진행할수록 테이블이 계속 늘어나지만, JSON은 하나의 컬럼에 작업 없이 여러 데이터를 저장할 수 있다.
2) 반정규화 조회 성능
- 여러 테이블을 조인하지 않고, 하나의 JSON 필드를 읽으면 여러 값을 동시에 조회할 수 있다.
- 여러 값이 함께 쓰이는 경우 한 번의 조회로 여러 값을 동시에 가져올 수 있다.
- 쓰기보다 조회 요청이 많을수록 효과적이다.
3) 외부 시스템 연동 데이터 저장
- 외부 시스템과 관련된 외부 시스템 작업 결과, 요청 결과 같은 정보는 외부시스템의 스키마를 직접 조절할 수 없다.
- 시간이 지나 외부 시스템의 스키마나 형태가 달라질 경우, 필요한 필드만 추출해서 사용할 수 있다.
4) 이벤트 및 로그성 데이터
- 이벤트 및 로그성 데이터는 종류마다 형태가 다르다. 구조가 정적이지 않은 데이터는 테이블로 설계하기 비효율적이다.
5) MVP 검증
- 신규 기능을 빠르게 검증하고 싶을 때 JSON으로 데이터를 저장한 후, 기능이 안정화되면 정규 컬럼으로 분리하여 저장한다.
4. JSON 타입의 단점
1) 데이터 무결성 보장 불가
- 테이블 컬럼은 기본키, 유니크, 외래키 제약조건을 통하여 데이터의 무결성을 보장할 수 있지만, JSON은 보장하지 않는다.
- 애플리케이션코드에서 보장할 수 있지만, 누락되거나 유지보수 비용이 증가한다.
2) 조회 성능이 낮다
- JSON은 내부키를 조건으로 검색하기 때문에 느리다.
- 테이블 컬럼은 인덱스의 B-트리를 사용하기 때문에 조회성능을 올릴 수 있지만, JSON 컬럼은 풀스캔 한다.
3) JOIN과 집계의 어려움
- 조인이나 집계 함수를 사용하기 위하여 항상 JSON 함수를 사용하여 JSON 내부의 데이터를 추출하는 작업이 필요하다.
- 조인 쿼리나 집계 함수를 사용하는 쿼리가 길어지거나 복잡해진다. -> 쿼리를 최적화하지 못하고, 속도는 느려진다.
4) 스키마 파악 불가
- 테이블 컬럼은 DDL을 통하여 볼 수 있지만, JSON 컬럼은 직접 파싱하여 분석해야 한다.
- 시기가 지나면 JSON 내부 데이터의 스키마가 조금씩 달라지는 경우가 있다.
1
2
3
4
5
6
7
8
9
10
11
// before
{
"address": "서울시 마포구"
}
// after
{
"address": {
"city": "서울",
"district": "마포구"
}
}
5) 저장 공간의 비효율성
- 일반 컬럼은 중복이 발생할 경우 정규화를 하여 중복을 최소화할 수 있지만, JSON은 중복을 막을 수 있는 방법이 없다.
- JSON의 데이터 하나만 바뀌어도 JSON 필드 모두를 백업하여 binlog에 저장해야 한다.
6) 부분 업데이트 비용
- 일반 컬럼은 해당 행만 생성, 수정, 삭제가 되지만, JSON은 내부의 데이터가 하나라도 추가, 삭제, 변경되면 내부적으로 JSON을 새로 생성한다.
- 부분 업데이트를 하기 위해서 JSON 전용 함수를 사용해야 한다.
5. JSON 타입 사용 가이드라인
1) JSON 타입 고려
- 주된 요청이 읽기 위주이고, 조회 시 JSON 필드의 내용 전체가 동시에 함께 사용되고, 자주 변경되지 않는다면 JSON 타입 사용을 고려해본다. (상품 속성, 외부 API 요청과 응답, 설정 및 환경 정보, 이벤트 페이로드)
2) 일반 컬럼 고려
- 일부 필드만 조회하거나, 자주 변경되거나, 조인 및 집계함수를 자주 사용한다면 일반 컬럼 사용을 고려해본다. -> 일부 스키마만 정규컬럼을 사용하고 나머지는 JSON 타입을 사용할수도 있다.
- 데이터의 무결성이 반드시 필요한 중요한 데이터라면 일반 컬럼을 사용한다. 무결성 보장을 위하여 해당 필드가 비즈니스의 핵심이라면 일반 컬럼을 사용하고, 부가 정보이면 JSON 사용을 고려해본다.
3) JSON 스키마
- JSON의 스키마를 팀 차원에서 별도의 문서로 관리한다.
- 키 네이밍 컨벤션
- 버전 필드 -> 버전 필드가 있다면 버전이 달라도 분기처리하여 처리 가능하다.
- 최대 깊이 및 크기 제한
1
2
3
4
5
6
7
8
{
"_schema_version": 2,
"delivery_type": "express",
"packaging": {
"eco_friendly": true,
"material": "paper"
}
}
4) JSON 크기 제한
1
2
-- MySQL CHECK 제약
ALTER TABLE products ADD CONSTRAINT chk_attr_size CHECK (JSON_LENGTH(attributes) < 100 AND LENGTH(attributes) < 10240);
- JSON의 크기제한 권장량은 아래와 같다.
- 일반적인 속성 데이터: 10KB 이하
- 이벤트 payload: 64KB 이하
- 원본 보관용: 별도 테이블 분리, 1MB 상한
5) JSON 인덱스 추가
- JSON 필드에 인덱스를 사용하고 싶다면 가상 컬럼을 생성한 후 인덱스를 추가한다.
1
2
3
ALTER TABLE orders
ADD delivery_type VARCHAR(20) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(options, '$.delivery_type'))) VIRTUAL,
ADD INDEX idx_delivery_type (delivery_type);
6) JSON -> 일반 컬럼 마이그레이션 검토 기준
- 아래 조건이 만족하면 JSON에서 일반 컬럼으로 변환을 검토해본다.
- JSON 키로 검색하는 쿼리가 많아지거나
- JSON 키의 NOT NULL 조건이 필요하거나
- JSON 키로 조인, 집계를 자주하거나
- JSON 키의 업데이트가 빈번히 이루어지거나
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.