본문으로 건너뛰기
8.4 정기 점검 체크리스트

8.4 정기 점검 체크리스트

지금까지의 Part I~VIII를 정리하면, 운영자가 주기적으로 봐야 하는 항목이 추려집니다. 일·주·월·분기 단위 점검 SQL을 한 곳에 모은 체크리스트입니다. 자동화의 출발점으로 사용합니다.

일 단위 (매일 / 알람 권장)

1. 활성 long transaction

SELECT pid, usename, datname, state, xact_start,
       now() - xact_start AS age, query
  FROM pg_stat_activity
 WHERE state IN ('active','idle in transaction','idle in transaction (aborted)')
   AND xact_start < now() - interval '5 minutes'
 ORDER BY xact_start;

5분 넘는 트랜잭션 = 알람을 받습니다. xmin horizon이 잡혀 vacuum이 막힙니다.

2. 락 대기 그래프

SELECT bl.pid AS blocked, bl.query AS blocked_query,
       kl.pid AS blocking, kl.query AS blocking_query
  FROM pg_stat_activity bl
  JOIN LATERAL (
    SELECT * FROM pg_stat_activity WHERE pid = ANY(pg_blocking_pids(bl.pid))
  ) kl ON true
 WHERE cardinality(pg_blocking_pids(bl.pid)) > 0;

비어 있어야 정상. 비어있지 않으면 즉시 확인합니다.

3. replication slot 정체

SELECT slot_name, slot_type, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag,
       wal_status
  FROM pg_replication_slots
 ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC NULLS LAST;

lag가 GB 단위로 자라거나 wal_status = 'lost'면 즉시 대응합니다. (12장 복제 참고)

4. errors in log (외부 로그 시스템에서)

ERROR, FATAL, PANIC 줄. 특히 deadlock detected, out of memory, could not extend file, database is being recovered 등.

주 단위

1. XID wraparound 안전 마진

SELECT datname,
       age(datfrozenxid) AS xid_age,
       round(age(datfrozenxid)::numeric / 2147483648, 3) AS used_ratio
  FROM pg_database
 ORDER BY age(datfrozenxid) DESC;

used_ratio가 0.5 (10억) 이상이면 주의. 0.7 이상이면 즉시 대응합니다.

2. dead tuple 누적

SELECT relname, n_live_tup, n_dead_tup,
       round(100.0 * n_dead_tup / nullif(n_live_tup, 0), 1) AS dead_pct,
       last_vacuum, last_autovacuum
  FROM pg_stat_user_tables
 WHERE n_dead_tup > 10000
 ORDER BY dead_pct DESC NULLS LAST
 LIMIT 20;

dead_pct > 20% 테이블이 자주 보이면 autovacuum 튜닝 검토합니다.

3. 미사용 인덱스

SELECT s.relname AS table, s.indexrelname AS index,
       pg_size_pretty(pg_relation_size(s.indexrelid)) AS size, s.idx_scan
  FROM pg_stat_user_indexes s
  JOIN pg_index i ON s.indexrelid = i.indexrelid
 WHERE s.idx_scan = 0 AND NOT i.indisunique
   AND pg_relation_size(s.indexrelid) > 10*1024*1024
 ORDER BY pg_relation_size(s.indexrelid) DESC;

idx_scan = 0이고 크면 drop 후보입니다. 단, 최근 추가된 인덱스나 주말 배치만 쓰는 인덱스는 예외.

4. 느린 쿼리 top

SELECT round(mean_exec_time::numeric, 1) AS avg_ms,
       calls,
       round(total_exec_time::numeric / 1000, 1) AS total_sec,
       LEFT(query, 80) AS query
  FROM pg_stat_statements
 ORDER BY total_exec_time DESC
 LIMIT 20;

pg_stat_statements를 활성한 클러스터의 표준입니다. total_exec_time(전체 부하)이 큰 쿼리부터.

월 단위

1. 인덱스 BLOAT

SELECT i.relname AS index,
       pg_size_pretty(pg_relation_size(i.oid)) AS size,
       round(s.avg_leaf_density, 1) AS density
  FROM pg_class i
  JOIN pgstatindex(i.oid) s ON true
 WHERE i.relkind = 'i' AND pg_relation_size(i.oid) > 100*1024*1024
 ORDER BY density LIMIT 10;

density < 70인 큰 인덱스 = REINDEX CONCURRENTLY 후보입니다.

2. 테이블 크기 추세

SELECT schemaname, relname,
       pg_size_pretty(pg_total_relation_size(relid)) AS total,
       pg_size_pretty(pg_relation_size(relid)) AS table,
       pg_size_pretty(pg_indexes_size(relid)) AS indexes,
       pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) AS toast_idx
  FROM pg_stat_user_tables
 ORDER BY pg_total_relation_size(relid) DESC
 LIMIT 20;

월간 추이 그래프로 그려 두면 용량 계획 자료. 갑작스러운 증가 = 알람을 받습니다.

3. 통계 신선도

SELECT relname, n_mod_since_analyze, last_analyze, last_autoanalyze
  FROM pg_stat_user_tables
 ORDER BY GREATEST(last_analyze, last_autoanalyze) NULLS FIRST
 LIMIT 10;

last_*가 한 달 이상 안 갱신된 테이블 = scale_factor 점검 또는 명시 ANALYZE.

4. autovacuum 워커 활동

SELECT pid, datname, relid::regclass, query
  FROM pg_stat_activity
 WHERE backend_type = 'autovacuum worker';

월 단위 통계: 일정 시간대에 autovacuum이 얼마나 동시에 도는지. autovacuum_max_workers 부족이면 동시에 다 차 있을 것.

5. WAL 생성량

SELECT wal_records, wal_fpi, wal_bytes, wal_buffers_full, stats_reset
  FROM pg_stat_wal;   -- PG 14+

월간 WAL 생성량은 아카이브 저장소 용량 계획과 복제 슬롯 보존 정책의 입력합니다.

분기·반기

1. REINDEX·pg_repack

월 점검에서 BLOAT 누적된 인덱스·테이블에 대해 분기 정비. (8.3)

2. 파티션 retention 정책 점검

pg_partman의 retention 결정이 여전히 정책에 맞는지 — 보존 기간 변경 검토합니다.

3. 메이저 업그레이드 계획

각 메이저는 5년 지원 (1.1). EOL 6개월 전엔 업그레이드 일정 잡기.

4. PgBackRest·Barman 복구 훈련

백업이 실제로 복원 가능한지 분기마다 검증합니다. (Part XI)

5. 보안 패치 / 마이너 업그레이드

마이너 패치는 분기마다 출시. 점검 시간 잡고 업그레이드 (Part XVIII).

알람 임계 기본

항목임계
Long transaction5분
Replication lag1GB 또는 1분
idle in transaction5분
XID wraparoundxid_age > 1억(WARN), 1.5억(CRIT)
디스크 사용률80% (WARN), 90% (CRIT)
Dead tuple 비율20%
Connection 사용률80%
Autovacuum 워커 모두 사용 중5분 이상
pg_stat_archiver last_failed_time5분

자동화 — Prometheus + Grafana

운영 표준은 점검 SQL을 메트릭으로 노출 + Grafana 대시보드.

도구역할
postgres_exporterpg_stat_*·통계 함수를 Prometheus 메트릭으로
node_exporterOS 메트릭 (CPU, 디스크 IO, 메모리)
Grafana대시보드, 알람 정책
Alertmanager / OpsGenie알람 라우팅

자세한 모니터링 셋업은 Part X 10.7~10.8.

정리

  • 일 단위: long transaction, 락, replication slot, error log
  • 주 단위: XID wraparound, dead tuple, 미사용 인덱스, 슬로우 쿼리
  • 월 단위: 인덱스 BLOAT, 테이블 크기 추세, 통계 신선도, autovacuum 활동, WAL 생성
  • 분기·반기: REINDEX/pg_repack, retention 검토, 메이저 업그레이드 계획, 복구 훈련
  • 자동화는 postgres_exporter + Grafana + Alertmanager가 표준

Part VIII 유지보수가 끝났습니다. 다음 Part IX에서는 인증·보안·권한을 봅니다.