13.1 튜닝 워크플로우
PostgreSQL 튜닝은 감으로 하지 않습니다. 측정 → 가설 → 검증의 사이클을 반복합니다. 운영자가 실제 사고나 정기 점검 때 따르는 워크플로우와, 순서 잘못 지키면 시간 낭비가 되는 함정을 정리합니다.
4단계 워크플로우
flowchart LR
M["측정<br/>(pg_stat_statements,<br/>EXPLAIN, OS metric)"]
H["가설 수립<br/>('이 쿼리가 이 인덱스를 못 쓰고 있어서')"]
C["변경<br/>(인덱스 추가 / 통계 갱신 /<br/> 파라미터 조정)"]
V["검증<br/>(다시 측정해 효과 확인)"]
M --> H --> C --> V --> M
classDef step fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
class M,H,C,V step
각 단계의 도구:
| 단계 | 도구 |
|---|---|
| 측정 | pg_stat_statements, EXPLAIN (ANALYZE, BUFFERS), pg_stat_io, OS metric |
| 가설 | 쿼리 plan + 통계 + 인덱스 구조 |
| 변경 | ALTER SYSTEM, CREATE INDEX CONCURRENTLY, ANALYZE, ALTER TABLE |
| 검증 | 같은 측정 도구, 하나씩 변경하고 효과 측정 |
우선순위 ladder
운영자가 사고를 마주쳤을 때 싼 도구부터:
| 우선 | 작업 | 비용 |
|---|---|---|
| 1 | ANALYZE (통계 갱신) | 즉시, 무료 |
| 2 | EXPLAIN 점검 (plan 미스 발견) | 무료 |
| 3 | 인덱스 추가 (CREATE INDEX CONCURRENTLY) | 분~시간 |
| 4 | 쿼리 재작성 (NOT EXISTS, OFFSET 0 등) | 분 |
| 5 | 파티셔닝 도입 | 일~주 |
| 6 | shared_buffers·work_mem·effective_cache_size 조정 | 재시작 필요 |
| 7 | OS·HW 튜닝 (huge_pages, NVMe, irqbalance) | 일~주 |
| 8 | 아키텍처 변경 (read replica, sharding, Citus) | 주~월 |
대부분 사고는 14에서 해결합니다. 68을 먼저 만지면 원인 파악 못 한 채 부수 효과만 봅니다.
측정 — “느린 쿼리가 무엇인가”
pg_stat_statements TOP
SELECT round(total_exec_time::numeric / 1000, 1) AS total_sec,
calls,
round(mean_exec_time::numeric, 2) AS mean_ms,
LEFT(query, 80) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;total_sec이 전체 운영 시간 대비 비율로. 1시간 운영 중 한 쿼리가 600초 차지 = 전체 1 cpu의 16%.
percentile 보기
-- p95, p99 슬로우
SELECT round(percentile_cont(0.95) WITHIN GROUP (ORDER BY mean_exec_time)::numeric, 1) AS p95_ms,
round(percentile_cont(0.99) WITHIN GROUP (ORDER BY mean_exec_time)::numeric, 1) AS p99_ms
FROM pg_stat_statements
WHERE calls > 100;평균보다 percentile이 사용자 체감과 가깝습니다.
auto_explain plan
특정 쿼리의 실제 plan을 잡으려면 auto_explain(10.6) 활성 + 로그 검색.
가설 — “왜 느린가”
수집한 plan을 보고 하나의 원인을 가설.
| 신호 | 가설 |
|---|---|
actual rows >> estimated rows | 통계 미스 |
Seq Scan 대형 테이블 | 인덱스 누락 |
Sort Method: external merge | work_mem 부족 |
Hash Batches > 1 | work_mem 부족 |
loops 큰 Nested Loop | 잘못된 조인 알고리즘 |
Heap Fetches > 0 (index-only scan) | VM 갱신 늦음 (vacuum) |
Buffers: shared read >> | cache miss, hot 쿼리에 shared_buffers 부족 |
BUFFERS hit rate < 95% | hot 워크로드인데 cache 작음 |
wait_event = LWLock | 공유 자원 경합 |
변경 — 한 번에 하나
같이 여러 변경을 적용하면 무엇이 효과 냈는지 알 수 없습니다. 변경의 황금률:
- 변경 사유 문서화
- 변경 전 측정값 기록
- 한 가지만 변경
- 변경 후 같은 측정
- 효과 없거나 부정 → 원복
운영 환경에서는 staging에서 먼저 검증 + 운영 적용합니다.
검증 — 효과 측정
같은 쿼리를 같은 데이터로:
EXPLAIN (ANALYZE, BUFFERS) <쿼리>;PG 13+ pg_stat_statements_reset()으로 변경 전후 별 카운터:
-- 변경 전
SELECT pg_stat_statements_reset();
-- (운영 1시간)
-- snapshot 저장
-- 변경 적용
SELECT pg_stat_statements_reset();
-- (운영 1시간)
-- snapshot 비교A/B로 같은 워크로드로 비교가 가장 정확합니다.
흔한 함정
1. 통계 안 갱신 후 인덱스 추가
"인덱스 만들었는데 옵티마이저가 안 쓰네요"ANALYZE 안 한 인덱스는 통계가 없어 옵티마이저가 비용 추정 부정확. 인덱스 만들고 꼭 ANALYZE.
2. work_mem을 무리하게 올림
"work_mem을 1GB로 올렸더니 OOM Killer가..."work_mem × 노드 × connections × parallel = 최악 메모리. 1.4 참고합니다.
3. 인덱스 폭증
"느린 쿼리마다 인덱스 추가했더니 INSERT가 느려졌어요"인덱스 N개 = INSERT/UPDATE 비용 N배. 미사용 인덱스 정기 정리(6.7).
4. shared_buffers를 50%로
"shared_buffers를 호스트 RAM 50%로 했는데 더 느려졌어요"OS page cache와 이중 캐싱 손해(1.4). 25% 출발 권장합니다.
5. EXPLAIN 한 번 본 뒤 결론
"이 쿼리가 안 느린데요?"운영 시간대마다 plan이 다를 수 있습니다. cache warmth·동시 부하·통계 신선도가 영향. 시간대별 측정합니다.
운영 워크로드 vs 분석 워크로드
같은 클러스터에 두 워크로드가 섞이면 튜닝이 정반대.
| 항목 | OLTP | 분석 |
|---|---|---|
work_mem | 작음 (8~16MB) | 큼 (256MB+, 세션 단위) |
shared_buffers | 25% | 25% (OS cache 의존) |
random_page_cost | 1.1 (SSD) | 1.1 |
effective_io_concurrency | 200 | 200~500 |
max_parallel_workers_per_gather | 0~2 | 4~8 |
| 인덱스 전략 | 좁고 많이 | 넓고 적게 |
| 파티셔닝 | 보존 정책 위주 | 대용량 + pruning |
권장: read standby에 분석 분리 + 워크로드별 튜닝.
시계열 — 추세를 본다
운영 사고는 급격한 변화에서 자주 발생합니다. 시계열 그래프(Grafana 등)에서:
| 지표 | 의미 |
|---|---|
| 평균 commit/sec | 부하 추세 |
| 평균 쿼리 latency p95 | 사용자 체감 |
| cache hit rate | 점진적 하락 = 데이터 증가, RAM 부족 |
| temp_files 비율 | work_mem 부족 신호 |
| autovacuum 시간 | dead tuple 누적 |
| WAL 생성량 | 쓰기 부하 |
| disk %util, iowait | I/O 한계 |
이 추세를 월간 회고에서 정기 점검 → 튜닝 우선순위 결정합니다.
정리
- 튜닝 = 측정 → 가설 → 변경 → 검증의 사이클
- 우선순위: 통계 → EXPLAIN → 인덱스 → 쿼리 → 파티션 → 파라미터 → OS·HW
- 한 번에 하나 변경, 변경 전후 측정
- OLTP와 분석은 다른 튜닝
- 추세 시계열로 사고 예방
다음 절(13.2)에서는 가장 효과 큰 영역 — 인덱스 전략을 봅니다.