“쿼리가 느린 게 아니라 모델이 거짓말을 하고 있다.”
1. 느린 쿼리는 결과, 원인은 모델 불일치
쿼리가 느릴 때 현장에서 제일 먼저 나오는 대응은 이겁니다.
- 인덱스 추가
- 힌트
- 서브쿼리 제거
- 조인 줄이기
근데 이걸로 안 끝나는 케이스는 거의 다 이겁니다.
모델이 현실을 제대로 표현하지 못하고 있다
그래서 DB가 “말을 돌려서” 답을 내느라 느려지는 겁니다.
2. 모델이 거짓말할 때 나타나는 전형적인 징후
아래 중 두 개 이상이면 99% 모델 문제입니다.
1️⃣ 의미가 모호한 컬럼
- status, type, flag, etc1
- 값이 1,2,3인데 의미는 문서에만 있음
→ 쿼리가 조건 분기 지옥으로 감
2️⃣ 시간 개념이 섞여 있음
- 생성일 / 수정일 / 적용일 / 종료일이 한 테이블에 뒤섞임
- “현재 유효한 것”을 구하기 위해 조건이 5개 이상
→ DB는 현재가 뭔지 계산해야 하는 상태
3️⃣ N:M을 숨긴 1:N
- 사실 이력 테이블인데 최신 row만 쓰는 구조
- group by + max + join
→ “최신”이라는 개념을 쿼리로 발명 중
4️⃣ 집계 책임이 DB에 떠넘겨짐
- 실시간 통계
- 조건별 합계
- 상태별 카운트
→ DB가 비즈니스 판단까지 대신함
3. 그래서 쿼리가 ‘느려 보이는’ 이유
DB는 거짓말을 그대로 믿고 실행합니다.
- “이 row는 하나야” → 실제로는 수백 개
- “상태 하나면 끝나” → 사실상 상태 머신
- “이 값은 불변” → 이력 덕지덕지
결과적으로 옵티마이저는
- 잘못된 카디널리티
- 틀린 실행 계획
- 불필요한 풀스캔
을 선택하게 됩니다.
쿼리가 느린 게 아니라, DB가 혼란스러운 상태인 거죠.
4. 해결은 튜닝이 아니라 “진실한 모델링”
이럴 때 정공법은 명확합니다.
✔ 상태를 쪼갠다
- 현재 상태 / 이력 상태 분리
- 의미 단위로 컬럼 분해
✔ 시간을 명시한다
- effective_from, effective_to
- “현재”를 컬럼으로 만들지 말고 테이블로 만든다
✔ 책임을 옮긴다
- 집계는 배치/캐시/전용 테이블
- 조회용 모델 따로 둠 (CQRS 개념)
✔ 쿼리가 아니라 질문을 바꾼다
- “이걸 한 번에 구할 수 있나?”
- “이걸 실시간일 필요가 있나?”
5. 한 줄 요약 (리뷰용)
DB가 느리게 답하는 건,
모델이 현실을 정직하게 설명하지 못하고 있기 때문이다.
LIST
'Developer > DB' 카테고리의 다른 글
| 인덱스는 공짜가 아니다. 쓰기 비용을 미래로 넘긴다 (0) | 2026.02.04 |
|---|---|
| 커넥션 풀의 변천사[old vs modern] (0) | 2026.01.02 |
| 📌 MariaDB vs PostgreSQL vs Oracle – 실무 개발자를 위한 핵심 비교 정리 (0) | 2025.11.26 |
| mybatis 변천사(feat. iBATIS ) (1) | 2025.09.22 |
| MyBatis 3에서는 SQL을 자동으로 실행해주는 기능 (0) | 2025.09.22 |
