반응형
MySQL에서 JSON은 유연한 대신 성능 이슈를 동반한다.
특히 WHERE 절에서 자주 조회하거나 JOIN에 얽히면 쿼리 속도가 급격히 느려질 수 있다.

⚠️ JSON 쿼리가 느려지는 이유
- JSON 컬럼은 일반적인 인덱스를 직접 생성할 수 없음
- profile->'$.name' 같은 경로 탐색은 모든 행을 검사해야 함
- JSON_EXTRACT, JSON_CONTAINS는 함수 호출 비용이 크다
즉, JSON 컬럼에 조건이 붙으면 인덱스가 무시되는 경우가 많다
반응형
✅ 해결 전략 1: 생성 컬럼(Generated Column) 활용
생성 컬럼은 JSON 내부 값을 꺼내 일반 컬럼처럼 쓰게 해준다.
그리고 여기에 인덱스를 걸 수 있다.
ALTER TABLE users
ADD COLUMN nickname VARCHAR(100)
GENERATED ALWAYS AS (JSON_UNQUOTE(profile->'$.nickname')) STORED,
ADD INDEX idx_nickname (nickname);
이제 아래 쿼리는 인덱스를 탄다.
SELECT * FROM users WHERE nickname = 'cooluser';
✅ STORED 옵션은 물리적으로 저장되어 성능이 더 좋음
✅ 자주 조회하는 JSON 필드는 이렇게 분리해두는 게 베스트
✅ 해결 전략 2: JSON 컬럼 자체를 조건에서 제외
- JSON은 저장용으로만 쓰고
- 실제 쿼리는 정규 컬럼에 저장된 값만 사용하는 구조로 변경
-- 잘못된 구조
SELECT * FROM orders
WHERE JSON_EXTRACT(order_meta, '$.status') = 'shipped';
-- 추천 구조
-- status 값을 별도 컬럼으로 분리
SELECT * FROM orders
WHERE status = 'shipped';
✅ 쿼리는 심플하고 빠르게 유지, JSON은 옵션/부가 정보 저장용으로 활용
✅ 해결 전략 3: WHERE 조건 최소화 + LIMIT 적극 활용
JSON을 조건에 쓰더라도 불가피하다면
결과 범위를 최소화하거나 LIMIT을 활용해 부하를 줄여야 한다.
SELECT * FROM users
WHERE JSON_UNQUOTE(profile->'$.job') = 'developer'
LIMIT 100;
✅ 데이터 양이 많을수록 LIMIT이 체감 속도에 큰 영향을 줌
✅ 해결 전략 4: 복합 조건으로 인덱스 유도
JSON 조건 단독으론 느리지만
다른 일반 컬럼과 함께 쓸 경우 인덱스를 활용할 수도 있다.
SELECT * FROM users
WHERE user_type = 'premium'
AND JSON_EXTRACT(profile, '$.marketing_optin') = 'true';
✅ user_type이 먼저 필터링되면서 스캔 범위를 줄여준다
반응형
🧠 실무 적용 예시
-- 자주 쓰는 검색 조건은 생성 컬럼으로 분리
ALTER TABLE products
ADD COLUMN brand VARCHAR(100)
GENERATED ALWAYS AS (JSON_UNQUOTE(details->'$.brand')) STORED,
ADD INDEX idx_brand (brand);
-- JSON 조건을 쿼리에서 완전히 제거하고 정규화
ALTER TABLE settings ADD COLUMN dark_mode BOOLEAN;
-- 배치 작업으로 dark_mode 값 추출 후 채우기
📌 정리
| 전략 | 설명 |
| 생성 컬럼 | JSON 내부 값을 일반 컬럼으로 꺼내 인덱스 적용 가능 |
| 조건 최소화 | WHERE에 JSON을 쓰는 범위를 줄이기 |
| JSON 필드 분리 | 자주 쓰는 값은 별도 컬럼으로 분리 (정규화) |
| 복합 조건 활용 | 일반 컬럼과 함께 쓰면 범위 필터링 가능 |
MySQL은 JSON 성능을 계속 개선 중이지만,
지금으로선 쿼리 최적화를 위한 구조적 설계가 가장 중요하다.
📘 공식 문서 참고
https://dev.mysql.com/doc/refman/8.0/en/generated-columns.html
반응형
'DB' 카테고리의 다른 글
| [MySQL] (GIS3️⃣) POLYGON과 공간 포함관계 쿼리 실습 (4) | 2025.07.18 |
|---|---|
| [MySQL] (GIS2️⃣) 공간 데이터 저장 및 조회 실습: POINT, LINESTRING 활용법 (1) | 2025.07.18 |
| [MySQL] (GIS1️⃣) GIS란? 공간 데이터 타입(POINT, POLYGON 등) 완전 정리 (1) | 2025.07.18 |
| [MySQL] (JSON6️⃣) 실무에서 JSON 타입을 쓸 때 주의할 점 (4) | 2025.07.17 |
| [MySQL] (JSON4️⃣) WHERE 절에서 JSON 조건 처리하는 방법 (0) | 2025.07.17 |
| [MySQL] (JSON3️⃣) JSON_EXTRACT, JSON_UNQUOTE, JSON_CONTAINS 파헤치기 (0) | 2025.07.17 |
| [MySQL] (JSON2️⃣) JSON_INSERT, JSON_SET, JSON_REPLACE 실전 활용법 (0) | 2025.07.17 |
| [MySQL] (JSON1️⃣) JSON 타입이란? 구조와 활용 예제 정리 (0) | 2025.07.17 |