본문으로 건너뛰기

3.4 pg_stat_statements

Database 전체 TPS가 늘었다는 사실만으로 어떤 query가 부하를 만들었는지 알 수 없습니다. pg_stat_statements는 정규화된 query별 호출 횟수, 총 실행 시간, 처리 row, block I/O 시간을 누적합니다. PostgreSQL Exporter의 stat_statements collector는 이 정보를 Prometheus metric으로 변환합니다.

PostgreSQL 설정

Extension은 shared preload가 필요합니다.

shared_preload_libraries = 'pg_stat_statements'
compute_query_id = auto
track_io_timing = on

재시작 후 database마다 extension을 만듭니다.

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

Exporter role의 search_path에는 pg_stat_statements를 설치한 schema가 포함되어야 합니다. Collector가 extension view를 schema 없이 조회하기 때문입니다.

track_io_timing은 I/O timing 수집 비용이 있으므로 환경에서 측정한 뒤 사용합니다. 최신 Linux에서는 대체로 작지만 workload와 platform에 따라 확인해야 합니다.

Collector 활성화

postgres_exporter \
--collector.stat_statements \
--collector.stat_statements.limit=100

Collector가 만드는 주요 metric은 다음과 같습니다.

MetricLabel의미
pg_stat_statements_calls_totaluser/datname/queryid실행 횟수 누계
pg_stat_statements_seconds_totaluser/datname/queryid총 실행 시간
pg_stat_statements_rows_totaluser/datname/queryid처리 row 누계
pg_stat_statements_block_read_seconds_totaluser/datname/queryidblock read 시간
pg_stat_statements_block_write_seconds_totaluser/datname/queryidblock write 시간

SQL 실행이 Query ID 누적 통계와 상위 N 수집을 거쳐 시계열이 되는 흐름

총부하가 큰 query

최근 5분 동안 DB 시간을 가장 많이 소비한 query ID를 찾습니다.

topk(10,
sum by (instance, datname, queryid) (
rate(pg_stat_statements_seconds_total[5m])
)
)

이 값은 초당 소비한 database execution seconds에 가깝습니다. 병렬 실행과 여러 session 때문에 합계가 wall-clock 1초를 넘기도 합니다.

평균 실행 시간

총 실행 시간 증가율을 호출 증가율로 나눕니다.

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
)

평균만 보면 한 번의 극단적 지연이나 tail latency를 숨길 수 있습니다. pg_stat_statements는 histogram이 아니므로 p95/p99를 제공하지 않습니다. 사용자 latency 분포는 애플리케이션 metric이나 trace에서 확인합니다.

SQL 원문을 label에 넣지 않는다

--collector.stat_statements.include_query는 query text mapping metric을 추가합니다. SQL은 길고 값이 다양하며 개인정보나 literal을 포함할 수 있습니다. 운영에서는 기본 비활성 상태를 유지하고, query ID를 다음 SQL로 원문과 연결합니다.

SELECT queryid,
calls,
total_exec_time,
mean_exec_time,
rows,
query
FROM pg_stat_statements
WHERE queryid = :queryid;

Exporter source도 이 collector를 기본 비활성화합니다. Busy server에서 query마다 series가 생겨 비용이 커질 수 있기 때문입니다.

Limit과 reset

Collector는 총 실행 시간 상위 statement를 제한된 수만 내보냅니다. 새로운 query가 top 목록에 들어오면 기존 series가 사라질 수 있으므로, 장기 ranking 데이터베이스처럼 사용하지 않습니다.

pg_stat_statements_reset()을 실행하면 counter가 reset됩니다. Prometheus rate()는 reset을 처리하지만 장기 비교에는 reset 시각을 annotation이나 운영 기록으로 남깁니다.

언제 켜는가

다음 조건을 만족할 때 활성화합니다.

  • query ID 수와 예상 series를 계산함
  • statement limit을 정함
  • SQL 원문 label을 비활성화함
  • scrape duration과 sample 수 변화를 관찰함
  • pg_stat_statements.max와 tracking 정책을 검토함

참고: PostgreSQL pg_stat_statements, Exporter stat_statements collector