6.4 Query Workload
느린 query를 찾는다는 말에는 서로 다른 세 문제가 섞여 있습니다.
- 호출 한 번이 느린 query
- 한 번은 빠르지만 너무 자주 실행되어 총부하가 큰 query
- 평소에는 빠르지만 특정 조건에서 tail latency가 큰 query
pg_stat_statements는 첫 번째와 두 번째 문제를 누적 통계로 보여 주지만 latency 분포는 제공하지 않습니다.
총 실행 시간 Top N
topk(10,
sum by (instance, datname, queryid) (
rate(pg_stat_statements_seconds_total[5m])
)
)
이 목록은 database CPU와 wait 시간을 많이 소비한 query 후보를 찾는 데 적합합니다.
평균 실행 시간 Top N
topk(10,
sum by (instance, datname, queryid) (
rate(pg_stat_statements_seconds_total[5m])
)
/
clamp_min(
sum by (instance, datname, queryid) (
rate(pg_stat_statements_calls_total[5m])
),
0.001
)
)
호출 수가 매우 적은 query가 위로 올라오기도 합니다. 최소 호출 rate 조건을 함께 적용하거나 table panel에서 calls/s를 나란히 표시합니다.
I/O 중심 query
topk(10,
sum by (instance, datname, queryid) (
rate(pg_stat_statements_block_read_seconds_total[5m])
)
)
I/O timing을 사용하려면 PostgreSQL track_io_timing이 활성화되어 있어야 합니다. 값이 0이라고 해서 I/O가 없다고 결론내리기 전에 설정을 확인합니다.
Query ID를 SQL과 연결
SELECT queryid,
calls,
total_exec_time,
mean_exec_time,
rows,
shared_blks_hit,
shared_blks_read,
temp_blks_written,
query
FROM pg_stat_statements
WHERE queryid = :queryid;
그다음 실제 parameter와 대표 실행계획을 확인합니다.
EXPLAIN (ANALYZE, BUFFERS, WAL, SETTINGS)
SELECT ...;
EXPLAIN ANALYZE는 query를 실제 실행합니다. 쓰기 SQL이나 production 대용량 query에는 안전한 재현 환경과 transaction rollback 여부를 먼저 검토합니다.
평균이 숨기는 것
평균 실행 시간이 20ms여도 일부 요청이 5초일 수 있습니다. 다음 signal을 함께 봅니다.
- 애플리케이션 query latency histogram
- OpenTelemetry database span
log_min_duration_statementslow log- lock wait와 connection pool wait
- parameter별 실행계획 차이
통계 reset과 계획 변경
pg_stat_statements_reset()이나 restart 뒤에는 이전 누적값과 직접 비교할 수 없습니다. 배포/ANALYZE/index 생성/parameter 변경 시각을 Grafana annotation으로 남깁니다.
Plan regression이 의심되면 다음을 비교합니다.
- Query ID와 호출 패턴이 같은가?
rows추정과 실제 row 차이가 커졌는가?- table 통계가 오래됐는가?
- generic plan과 custom plan이 갈리는가?
- cache 상태와 I/O 조건이 같은가?
개선 우선순위
총 실행 시간 기여도가 크고 개선 가능성이 높은 query부터 다룹니다. 한 번에 30초지만 하루 한 번 실행되는 batch보다, 10ms를 줄이면 초당 수백 번 이익을 얻는 query가 먼저일 수 있습니다. 사용자 critical path와 error budget도 함께 반영합니다.