8.2 ANALYZE 통계 갱신 전략
5.3에서 통계의 구조를 봤다면, 운영자가 ANALYZE를 언제·어떻게 호출할지의 전략을 다룹니다. autovacuum의 analyze가 따라오지 못하는 시나리오와, 큰 테이블에서 분포가 빠르게 바뀌는 경우의 대응이 핵심입니다.
ANALYZE 트리거 — 4가지 경로
| 경로 | 동작 |
|---|---|
| autovacuum의 자동 ANALYZE | n_mod_since_analyze가 임계 초과 시 |
수동 ANALYZE | 운영자가 명시 호출 |
| ETL/대량 INSERT 직후 명시 | 자동 트리거를 기다리지 않음 |
VACUUM ANALYZE 묶음 | vacuum 끝나면 곧바로 |
autovacuum analyze가 늦는 경우
기본 임계:
autovacuum_analyze_threshold = 50
autovacuum_analyze_scale_factor = 0.110M row 테이블이면 1M row가 변경돼야 트리거. 그 사이 옵티마이저는 옛 통계로 plan을 만듭니다. 결과적으로 plan 미스가 자주 발생합니다.
대응:
ALTER TABLE big_table SET (
autovacuum_analyze_scale_factor = 0.02, -- 2% 변경에 트리거
autovacuum_analyze_threshold = 1000
);ETL 직후 명시 호출
대량 INSERT/UPDATE 직후는 자동 트리거를 기다릴 시간 없이 즉시 명시:
-- 큰 임포트
COPY events_2026_05 FROM '/tmp/events.csv';
-- 곧바로
ANALYZE events_2026_05;
-- 또는 전체 부모(파티션)에
ANALYZE events;ETL이 끝났는데 통계 갱신이 빠지면, 다음 쿼리의 plan이 빈 테이블 가정으로 만들어져 nested loop 폭주 같은 사고가 납니다.
sample 크기 — statistics_target
ANALYZE는 샘플링 기반입니다. 통계 정확도는 샘플 크기에 비례.
default_statistics_target = 100 -- 기본- 한 컬럼당 약
target × 300row를 샘플 - target = 100 → 30k row 샘플
- target = 1000 → 300k row 샘플
복잡한 분포(매우 skew된 데이터, MCV가 많이 필요한 경우)는 target 상향 권장합니다.
-- 컬럼 단위
ALTER TABLE orders ALTER COLUMN status SET STATISTICS 1000;
ANALYZE orders;
-- 클러스터 기본 (신중히)
ALTER SYSTEM SET default_statistics_target = 200;
SELECT pg_reload_conf();target을 너무 키우면 ANALYZE 자체가 무거워집니다. 운영 권장:
- 일반: 100 (기본)
- 옵티마이저가 자주 잘못 추정하는 컬럼: 500~1000
- catalog 같은 작은 테이블: 100
컬럼 단위 ANALYZE (PG 9.5+)
특정 컬럼만 갱신해 빠르게.
ANALYZE orders (status, created_at);자주 갱신되는 컬럼만 일정 짧게 돌리는 패턴에 유용합니다.
extended statistics 갱신
CREATE STATISTICS (5.3 참고)도 ANALYZE에 의해 갱신됩니다. 자동입니다.
CREATE STATISTICS orders_geo (dependencies, ndistinct) ON city, country FROM orders;
ANALYZE orders; -- extended statistics도 같이 갱신진단
-- 통계가 오래된 테이블
SELECT schemaname, relname, n_mod_since_analyze,
last_analyze, last_autoanalyze,
n_live_tup
FROM pg_stat_user_tables
ORDER BY GREATEST(last_analyze, last_autoanalyze) NULLS FIRST
LIMIT 10;
-- 분포 확인
SELECT attname, n_distinct, most_common_vals, most_common_freqs
FROM pg_stats
WHERE schemaname = 'public' AND tablename = 'orders';n_mod_since_analyze가 크고 last_analyze가 오래된 테이블 = 통계 갱신 대상입니다.
파티션과 ANALYZE
선언적 파티션 부모의 통계는 자식 통계의 합산이 아니라 별도로 관리됩니다.
ANALYZE events; -- 부모 통계만 갱신 (PG 17 이전엔 자식까지 일부)
ANALYZE events_2026_05; -- 한 파티션
ANALYZE ROOT events; -- PG 14+에서 부모만자식 파티션의 통계는 자식 단위로 갱신해야 정확합니다. autovacuum은 자식별로 자동 트리거되지만, ETL 직후는 명시합니다.
ANALYZE의 비용
| 측면 | 메모 |
|---|---|
| 락 | SHARE UPDATE EXCLUSIVE — SELECT/INSERT 가능 |
| I/O | 샘플링 — 큰 테이블에서도 일정 시간 |
| CPU | 정렬·해시 등 분석 작업 |
BUFFER_USAGE_LIMIT (PG 16+) | 메모리 한도 조절 가능 |
운영 중 안전하지만, 매우 큰 테이블 + 높은 target은 수 분 걸릴 수 있습니다.
운영 표준
# 자동화 — 매일 새벽 분포 빠른 컬럼 갱신
0 3 * * * psql -c "ANALYZE orders (status); ANALYZE events (created_at);"- 매 ETL/COPY 직후 ANALYZE 호출
- 큰 테이블의 scale_factor 0.02~0.05
- 옵티마이저가 자주 틀리는 컬럼은 target 상향
- 컬럼 간 상관관계는
CREATE STATISTICS - plan 미스 진단의 첫 단계는 ANALYZE
정리
- ANALYZE는 옵티마이저의 입력인 통계를 갱신
- autovacuum의 analyze는 큰 테이블에서 늦음 — scale_factor 조절
- ETL 직후 명시 호출이 운영 표준
default_statistics_target은 보통 100이면 충분합니다. 특정 컬럼만 500~1000- 파티션은 자식 단위 ANALYZE가 정확
- plan 미스 진단의 first step
다음 절(8.3)에서는 인덱스 BLOAT 정리 — REINDEX의 운영 방법을 봅니다.