본문으로 건너뛰기
8.2 ANALYZE 통계 갱신 전략

8.2 ANALYZE 통계 갱신 전략

5.3에서 통계의 구조를 봤다면, 운영자가 ANALYZE를 언제·어떻게 호출할지의 전략을 다룹니다. autovacuum의 analyze가 따라오지 못하는 시나리오와, 큰 테이블에서 분포가 빠르게 바뀌는 경우의 대응이 핵심입니다.

ANALYZE 트리거 — 4가지 경로

경로동작
autovacuum의 자동 ANALYZEn_mod_since_analyze가 임계 초과 시
수동 ANALYZE운영자가 명시 호출
ETL/대량 INSERT 직후 명시자동 트리거를 기다리지 않음
VACUUM ANALYZE 묶음vacuum 끝나면 곧바로

autovacuum analyze가 늦는 경우

기본 임계:

autovacuum_analyze_threshold = 50
autovacuum_analyze_scale_factor = 0.1

10M 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 × 300 row를 샘플
  • 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
plan이 갑자기 이상하다면 우선 ANALYZE. 90% 이상의 plan 미스가 통계 신선도 또는 target 부족이 원인입니다. ALTER, REINDEX 같은 무거운 작업 전에 ANALYZE 한 번이 정석입니다.

정리

  • ANALYZE는 옵티마이저의 입력인 통계를 갱신
  • autovacuum의 analyze는 큰 테이블에서 늦음 — scale_factor 조절
  • ETL 직후 명시 호출이 운영 표준
  • default_statistics_target은 보통 100이면 충분합니다. 특정 컬럼만 500~1000
  • 파티션은 자식 단위 ANALYZE가 정확
  • plan 미스 진단의 first step

다음 절(8.3)에서는 인덱스 BLOAT 정리 — REINDEX의 운영 방법을 봅니다.