주문 목록 화면이 점점 느려진다. 주문·고객·결제 테이블을 조인하고 건수까지 세서 보여주는 쿼리인데, 데이터가 수백만 건을 넘으면서 응답이 눈에 띄게 늘어졌다.
언제: 거의 항상 이게 먼저다. EXPLAIN을 떠보면 대부분 풀스캔이나 정렬이 범인이고, 인덱스 하나 또는 쿼리 한 줄 수정으로 끝난다.
비용: 쓰기가 약간 느려지고 디스크를 조금 더 쓴다. 마이그레이션 한 번이면 되고, 잘못 만들었으면 그냥 지우면 된다 — 되돌리기 쉽다.
언제: 인덱스를 제대로 태웠는데도 조인·집계 자체가 비싸서 안 내려올 때. 특히 읽기가 쓰기보다 압도적으로 많은 화면에서 매번 COUNT/SUM을 새로 계산하고 있을 때.
비용: 같은 데이터가 두 곳에 생겨서 동기화 코드를 평생 관리해야 한다. 어긋나면 '숫자가 안 맞아요' 같은, 재현도 어렵고 제일 잡기 싫은 버그가 된다.
기본은 인덱스부터다. EXPLAIN으로 실제 실행 계획을 보고 인덱스나 쿼리 수정으로 잡는 게 먼저다. 인덱스가 제대로 타는 걸 확인했는데도 조인·집계 비용 때문에 p95가 안 내려오고, 그 화면의 읽기가 쓰기보다 100배쯤 많을 때만 비정규화로 넘어간다.
- 인덱스를 태운 뒤에도 목록 조회 p95가 300ms 아래로 안 내려옴
- EXPLAIN ANALYZE에서 비용 대부분이 스캔이 아니라 조인 5개 이상과 집계 단계에서 나옴
- 읽기:쓰기 비율이 100:1쯤 되는 화면인데 매 요청마다 COUNT/SUM을 다시 계산하고 있음
- pg_stat_statements 상위 1등이 계속 같은 집계 쿼리이고 DB CPU가 70%대에 붙어 있음
🔧EXPLAIN ANALYZEpg_stat_statementsslow query logDatadog APM
“I default to an index — I only denormalize when EXPLAIN shows the join itself is the cost, not the scan.”
checking microphone…