🔗 실험 배경
이전 포스트에서 updated_at, created_at, title 기준으로 인덱스를 생성하여 단일 조건 기반 조회 성능을 개선한 바 있습니다.
이번에는 실전에서 사용하는 쿼리를 사용해서 실험해보려고 합니다.
실전 호출 api : GET http://localhost:8080/api/feeds?keyword=더미&page=0&size=10
🧪 1차 실험 조건
| 실험 항목 | 조건 |
| 더미 데이터 | 10000건 |
| 정렬 기준 | updated_at DESC |
| 조인 | 좋아요, 댓글, 이미지, 유저 |
| 시행 횟수 | 10회 |
쿼리문 :
SELECT nf.id,
u.id AS user_id,
u.nickname,
nf.title,
nf.content,
nf.updated_at,
COUNT(DISTINCT nfl.id) AS like_count,
COUNT(DISTINCT c.id) AS comment_count,
MIN(f.path) AS thumbnail,
nf.view_count
FROM news_feeds nf
LEFT JOIN news_feed_likes nfl ON nf.id = nfl.news_feeds_id
LEFT JOIN comments c ON nf.id = c.news_feed_id
LEFT JOIN news_files f ON nf.id = f.news_feed_id
JOIN users u ON nf.user_id = u.id
WHERE nf.deleted_at IS NULL
AND (nf.title LIKE '%더미%' OR nf.content LIKE '%더미%')
AND nf.updated_at BETWEEN '2025-04-01 00:00:00' AND '2025-05-31 23:59:59'
GROUP BY nf.id, u.id, u.nickname, nf.title, nf.content, nf.updated_at, nf.view_count
ORDER BY nf.updated_at DESC;
📊 1차 실험 결과
| 실험 유형 | 총 평균 시간(ms) | Execution 평균 (ms) | Fetching 평균 (ms) | 최소~최대 시간 (ms) |
| 인덱스 없음 | 321.6 | 140.6 | 181.0 | 183 ~ 476 |
| 단일 인덱스 (updated_at) |
307.7 | 139.8 | 167.9 | 183 ~472 |
| 복합 인덱스 (title, updated_at) |
300.9 | 138.5 | 162.4 | 182 ~461 |
| 최적화 인덱스 (title, updated_at, deleted_at) |
260.6 | 133.4 | 127.2 | 182 ~ 396 |
기대한 만큼 성능의 개선이 나오지 않음
🤔 개선이 미미한 원인
1️⃣ LIKE '%더미%' 패턴 검색의 인덱스 무력화
2️⃣ GROUP BY 대상 컬럼이 너무 많고 JOIN이 많음
🧪 2차 실험 조건
| 실험 항목 | 조건 |
| 더미 데이터 | 1,000,000건 |
| 검색 조건 | LIKE vs MATCH |
| 정렬 기준 | updated_at DESC |
| 조인 | 좋아요, 댓글, 이미지, 유저 |
| 시행 횟수 | 10회 |
더보기
SELECT nf.id,
u.id AS user_id,
u.nickname,
nf.title,
nf.content,
nf.updated_at,
COUNT(DISTINCT nfl.id) AS like_count,
COUNT(DISTINCT c.id) AS comment_count,
MIN(f.path) AS thumbnail,
nf.view_count
FROM news_feeds nf
LEFT JOIN news_feed_likes nfl ON nf.id = nfl.news_feeds_id
LEFT JOIN comments c ON nf.id = c.news_feed_id
LEFT JOIN news_files f ON nf.id = f.news_feed_id
JOIN users u ON nf.user_id = u.id
WHERE nf.deleted_at IS NULL
AND (nf.title LIKE '%더미%' OR nf.content LIKE '%더미%')
AND nf.updated_at >= '2025-04-01 00:00:00'
AND nf.updated_at <= '2025-05-31 23:59:59'
GROUP BY nf.id, u.id, u.nickname, nf.title, nf.content, nf.updated_at, nf.view_count
ORDER BY nf.updated_at DESC;
SELECT nf.id,
u.id AS user_id,
u.nickname,
nf.title,
nf.content,
nf.updated_at,
COUNT(DISTINCT nfl.id) AS like_count,
COUNT(DISTINCT c.id) AS comment_count,
MIN(f.path) AS thumbnail,
nf.view_count
FROM news_feeds nf
LEFT JOIN news_feed_likes nfl ON nf.id = nfl.news_feeds_id
LEFT JOIN comments c ON nf.id = c.news_feed_id
LEFT JOIN news_files f ON nf.id = f.news_feed_id
JOIN users u ON nf.user_id = u.id
WHERE nf.deleted_at IS NULL
AND MATCH(nf.title, nf.content) AGAINST('+더미' IN BOOLEAN MODE)
AND nf.updated_at BETWEEN '2025-04-01 00:00:00' AND '2025-05-31 23:59:59'
GROUP BY nf.id, u.id, u.nickname, nf.title, nf.content, nf.updated_at, nf.view_count
ORDER BY nf.updated_at DESC;
📊 실험 결과 요약
| 실험 조건(10회) | 평균 실행 시간 (초) | 개선율 (기준: 기본 인덱스) |
| 기본 인덱스(PK, FK만) | 11.5s | - |
| 복합 인덱스 (title, content, updated_at, deleted_at) | 7.6s | 🔽 약 33.9% 향상 |
| MATCH 사용 (FULLTEXT 인덱스, ft_min_word_len=2) | 13.6s | 🔺 약 18.3% 느려짐 |
🔍 분석
- LIKE 조건에서는 복합 인덱스(title, content, updated_at, deleted_at)가 명확한 성능 향상을 보임
- MATCH 사용 시, 오히려 실행 시간이 증가
- 이는 MATCH가 FULLTEXT 인덱스를 사용하더라도 JOIN + GROUP BY가 복잡한 경우에는 오히려 오버헤드가 발생함을 의미
- 또한, BOOLEAN MODE를 쓰더라도 결과 필터링에 따른 로우 수 증가가 성능 저하로 이어졌을 가능성 존재
✅ 결론
- 복잡한 조인/필터/정렬 조건이 포함된 실사용 쿼리에서는 FULLTEXT MATCH보다는 복합 인덱스 + LIKE 조건이 더 효과적
- 인덱스 구성은 단순 조건 필터링에만 초점을 둘 것이 아니라, 정렬 기준과 함께 설계하는 것이 중요함
- 쿼리 튜닝은 무조건 MATCH → LIKE보다 빠르다는 통념을 다시 생각해볼 필요가 있음
'SPRING' 카테고리의 다른 글
| [SPRING] 트랜잭션(Transaction) (2) | 2025.04.17 |
|---|---|
| [SPRING] 인터셉터 AOP (1) | 2025.04.15 |
| [SPRING] 뉴스피드 과제 인덱스 성능 실험 (1) | 2025.04.13 |
| [Spring] 스프링 테스트 코드 (3) | 2025.04.10 |
| [Spring] 인덱싱 (0) | 2025.04.09 |