10.6 auto_explain
auto_explain은 슬로우 쿼리의 EXPLAIN ANALYZE 결과를 자동으로 로그에 남기는 표준 확장입니다. 사후 분석에 그 시점의 plan을 확인하는 유일한 방법. pg_stat_statements가 요약 통계라면 auto_explain은 개별 trace.
활성
# postgresql.conf
shared_preload_libraries = 'pg_stat_statements,auto_explain'
auto_explain.log_min_duration = '1s' # 1초 넘는 쿼리만
auto_explain.log_analyze = on # 실제 실행 시간 (BUFFERS·timing 포함)
auto_explain.log_buffers = on # cache hit/read 통계
auto_explain.log_timing = on # actual time (off하면 약간 오버헤드 ↓)
auto_explain.log_verbose = on # 컬럼 정보
auto_explain.log_triggers = on # 트리거 시간
auto_explain.log_settings = on # 비기본 설정 표시 (PG 12+)
auto_explain.log_format = json # text/json/yaml/xml
auto_explain.log_nested_statements = on # 함수 내부 쿼리도 포함
auto_explain.log_level = log # log_min_messages 단계
auto_explain.sample_rate = 1.0 # 1.0 = 모든 슬로우 쿼리. 0.1 = 10%만auto_explain은 함수 안에서만 켤 수도 있습니다.
LOAD 'auto_explain';
SET auto_explain.log_min_duration = '0';
SET auto_explain.log_analyze = on;
-- 이 세션의 모든 쿼리가 EXPLAIN ANALYZE 자동 출력
SELECT ...출력 예시
text 포맷:
2026-05-23 11:00:01 KST [12345] LOG: duration: 1503.421 ms
plan:
Query Text: SELECT * FROM orders WHERE user_id = $1
Index Scan using orders_user_id_idx on orders (cost=0.42..823.51 rows=1000 width=64)
Index Cond: (user_id = $1)
Buffers: shared hit=14 read=200JSON 포맷은 자동 분석 도구가 파싱하기 쉬워 운영에서 더 선호.
트레이드오프
| 옵션 | 비용 |
|---|---|
log_analyze = on | 매 쿼리 실행을 측정 — 일반적으로 slowdown ~ 1% |
log_timing = on | 노드 단위 timing — Linux는 매우 가벼움, 가상 환경에서 약간 비용 |
log_min_duration = 0 (모든 쿼리) | 로그 폭증 + 측정 비용. 디버깅 외 금지 |
sample_rate = 0.1 | 10% 샘플 — 부하 줄임 |
log_format = json | 파싱 용이, 약간 큼 |
운영 권장 출발:
auto_explain.log_min_duration = '1s'
auto_explain.log_analyze = on
auto_explain.log_buffers = on
auto_explain.log_format = json
auto_explain.sample_rate = 1.0부하가 큰 시스템에서 sample_rate를 0.1로 낮춰도 슬로우 쿼리 자체가 드물면 충분한 샘플 확보.
분석 도구 짝
| 도구 | 활용 |
|---|---|
| explain.depesz.com | 텍스트 plan 시각화 |
| explain.dalibo.com | JSON plan |
| pgBadger | auto_explain의 plan 통계 |
| Loki + Grafana | JSON 로그 검색 |
함수 내부 쿼리
기본은 최상위 쿼리만 측정합니다. PL/pgSQL 함수 내부의 SQL을 보려면:
auto_explain.log_nested_statements = on함수 내부의 각 SQL이 별도로 측정되어 로그.
auto_explain vs EXPLAIN ANALYZE 직접 호출
| 측면 | auto_explain | EXPLAIN ANALYZE |
|---|---|---|
| 발생 시점 | 운영 중 실시간 자동 | 운영자가 수동 호출 |
| 재현 어려운 쿼리 | 잡힘 | 못 잡음 (한 번 발생) |
| 운영 부하 | 약간 (sample_rate로 제어) | 0 (호출 안 함) |
| 데이터 부작용 | 없음 | INSERT/UPDATE/DELETE는 실제 적용됨 |
운영 중 간헐적 느린 쿼리의 plan을 잡는 거의 유일한 방법.
pg_stat_statements와의 차이
| 항목 | pg_stat_statements | auto_explain |
|---|---|---|
| 데이터 형태 | 누적 카운터 | 개별 trace |
| 어디 저장 | shared memory | log 파일 |
| 정규화 | 쿼리 패턴별 합산 | 개별 호출별 |
| 사용 | 어떤 쿼리가 무거운가 | 그 쿼리가 어떤 plan을 받았나 |
둘은 대체재가 아니라 보완재. 함께 켜는 게 표준입니다.
활용 워크플로
flowchart LR
STATS["pg_stat_statements<br/>(요약)"] --> TOP["TOP 슬로우 쿼리 식별"]
AE["auto_explain<br/>(개별 plan 캡처)"] --> LOG["로그에서 plan 찾기"]
TOP --> JOIN["queryid 매칭"]
LOG --> JOIN
JOIN --> FIX["plan 미스 원인 파악·튜닝"]
classDef stat fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef ae fill:#fed7aa,stroke:#c2410c,color:#7c2d12
classDef fix fill:#d1fae5,stroke:#047857,color:#064e3b
class STATS,TOP stat
class AE,LOG ae
class JOIN,FIX fix
PG 14+의 queryid로 두 도구 결과를 매칭 가능합니다.
주의
| 주의 | 메모 |
|---|---|
log_min_duration = 0은 디버깅용 | 운영에서는 1s 이상만 |
| 로그 디스크 폭증 가능 | rotation·외부 수집 필수 |
| 함수 내부 SQL의 paramter 노출 | 민감 정보 — log_parameter_max_length로 잘라낼 수 있음 |
| crash 분석 후엔 로그가 곧 plan 원본 | 백업 보존 정책에 포함 |
정리
- auto_explain = 슬로우 쿼리의 EXPLAIN을 자동 로그
- 운영 표준:
log_min_duration = 1s,log_analyze = on, JSON 포맷 - 함수 내부 쿼리도
log_nested_statements = on - pg_stat_statements와 짝 — 요약 + 개별 plan
- explain.depesz·explain.dalibo와 결합해 빠른 분석
- 측정 비용은 1% 내외 — sample_rate로 더 줄일 수 있음
다음 절(10.7)에서는 외부 모니터링 도구 — Prometheus, Datadog, pgwatch2와의 통합을 봅니다.