8.1 VACUUM과 autovacuum
VACUUM은 MVCC가 남긴 dead tuple을 정리해 공간을 재사용 가능하게 만들고, XID wraparound을 막기 위해 tuple을 freeze하는 PostgreSQL 운영의 핵심 작업입니다. 수동 실행도 가능하지만 대부분의 시스템은 autovacuum이 알아서 돌게 둡니다. 둘의 동작과 운영 시 조절 포인트를 봅니다.
VACUUM의 일
| 작업 | 목적 |
|---|---|
| dead tuple 정리 | DELETE/UPDATE가 남긴 죽은 row를 공간 회수 후보로 표시. 다음 INSERT가 재사용 |
| Free Space Map(FSM) 갱신 | 어느 페이지에 빈 공간이 있는지 (1.5) |
| Visibility Map(VM) 갱신 | all-visible·all-frozen 비트 (4.3) |
| 인덱스 정리 | 죽은 tuple을 가리키던 인덱스 entry 제거 |
| FREEZE | 오래된 XID를 FrozenXID로 바꿔 wraparound 안전 |
pg_class.relfrozenxid 갱신 | 클러스터 wraparound 추적 (3.2) |
dead tuple을 즉시 디스크에서 삭제하지는 않습니다. 공간을 재사용 가능 표시만 합니다. 실제 디스크 회수는 VACUUM FULL만 가능 (테이블 락 잡고 재구성, 운영 중 권장 안 함).
VACUUM의 종류
| 명령 | 효과 | 락 |
|---|---|---|
VACUUM | 표준. dead 정리 + FSM/VM 갱신 | SHARE UPDATE EXCLUSIVE — SELECT/INSERT 가능 |
VACUUM FULL | 테이블 재구성 + 디스크 축소 | ACCESS EXCLUSIVE — 모든 쿼리 차단 |
VACUUM FREEZE | aggressive freeze | SHARE UPDATE EXCLUSIVE |
VACUUM ANALYZE | VACUUM + ANALYZE 동시 | 동일 |
VACUUM (INDEX_CLEANUP off, ...) | 인덱스 정리 생략 (PG 12+) | 동일 |
PG 13+에서는 병렬 vacuum 가능 — 인덱스 정리를 병렬로 (PARALLEL N 옵션).
VACUUM (VERBOSE, ANALYZE, PARALLEL 4) orders;autovacuum 동작
postmaster가 띄우는 autovacuum launcher가 주기적으로 깨어나 vacuum이 필요한 테이블을 찾고, autovacuum worker를 fork해 작업을 시킵니다.
트리거 조건
각 테이블별로 카운터를 봅니다.
| 카운터 | 임계 |
|---|---|
n_dead_tup | autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor × n_live_tup 초과 |
n_ins_since_vacuum (PG 13+) | autovacuum_vacuum_insert_threshold + autovacuum_vacuum_insert_scale_factor × n_live_tup 초과 |
n_mod_since_analyze | autovacuum_analyze_threshold + autovacuum_analyze_scale_factor × n_live_tup 초과 (ANALYZE) |
relfrozenxid 나이 | autovacuum_freeze_max_age(기본 2억) 초과 → aggressive |
기본값:
| 파라미터 | 기본 |
|---|---|
autovacuum_vacuum_threshold | 50 |
autovacuum_vacuum_scale_factor | 0.2 (20%) |
autovacuum_vacuum_insert_threshold | 1000 |
autovacuum_vacuum_insert_scale_factor | 0.2 |
autovacuum_analyze_threshold | 50 |
autovacuum_analyze_scale_factor | 0.1 |
autovacuum_max_workers | 3 |
autovacuum_naptime | 1min |
큰 테이블에서 0.2가 너무 큼
10M row 테이블이면 dead 2M가 쌓여야 vacuum이 돕니다. 그 사이 BLOAT이 자랍니다.
ALTER TABLE big_table SET (
autovacuum_vacuum_scale_factor = 0.02,
autovacuum_analyze_scale_factor = 0.02
);큰 테이블은 항상 테이블별로 작은 값 권장합니다.
I/O 제한
autovacuum이 시스템에 부담을 주지 않도록 vacuum_cost 메커니즘으로 I/O를 제한합니다.
| 파라미터 | 기본 | 의미 |
|---|---|---|
autovacuum_vacuum_cost_delay | 2ms (PG 12+) | 비용 한도 도달 시 쉬는 시간 |
autovacuum_vacuum_cost_limit | -1 (= vacuum_cost_limit 200) | 한 라운드의 비용 한도 |
vacuum_cost_page_hit | 1 | 캐시 hit 페이지 비용 |
vacuum_cost_page_miss | 2 | cache miss 비용 |
vacuum_cost_page_dirty | 20 | dirty 만드는 페이지 비용 |
비용이 limit에 도달하면 delay만큼 쉬고 다시 시작합니다. 결과적으로 autovacuum이 전체 I/O의 일부만 사용합니다.
큰 시스템에서는 autovacuum_vacuum_cost_limit을 2000~10000 정도로 키워 더 빠르게 돌도록 합니다.
ALTER SYSTEM SET autovacuum_vacuum_cost_limit = 2000;
SELECT pg_reload_conf();진단
-- 현재 돌고 있는 autovacuum
SELECT pid, datname, relid::regclass, query
FROM pg_stat_activity
WHERE backend_type = 'autovacuum worker';
-- 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
ORDER BY n_dead_tup DESC
LIMIT 10;
-- 진행률 (PG 12+)
SELECT * FROM pg_stat_progress_vacuum;dead_pct가 20%+ 인 테이블이 자주 보이면 autovacuum이 못 따라오고 있다는 신호입니다.
XID wraparound 대비
SELECT datname,
age(datfrozenxid) AS xid_age,
pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY xid_age DESC;xid_age가 1.5억을 넘으면 autovacuum이 aggressive 모드(FREEZE)로 들어갑니다. 그래도 진행이 안 되면 17억 가까이서 read-only 강제 전환 위험합니다.
원인 — 거의 항상 긴 트랜잭션 또는 idle replication slot이 xmin horizon을 잡고 있어 vacuum이 무용합니다.
VACUUM이 안 되는 흔한 원인
| 원인 | 확인 |
|---|---|
| 긴 트랜잭션 | pg_stat_activity.xact_start 오래된 row |
| 잊혀진 prepared transaction | pg_prepared_xacts |
| idle replication slot | pg_replication_slots의 xmin, catalog_xmin |
autovacuum 비활성 또는 enabled = false | ALTER TABLE ... SET (autovacuum_enabled = ...) |
| cost limit 너무 작음 | autovacuum이 느려서 못 따라옴 |
운영 권장
- autovacuum은 항상 켜 두기 — 끄는 건 거의 사고의 시작
- 큰 테이블은 scale_factor를 0.02~0.05로 별도 설정
- idle in transaction timeout 설정 (3.1) — vacuum 차단 방지
pg_stat_progress_vacuum으로 진행률 추적VACUUM FULL은 운영 중 금지 —pg_repack대신- xid_age 알람 1.5억 임계 모니터링
autovacuum = off는 사고의 시작. 일시적 디스크 절약 같은 이유로 끄면 BLOAT이 폭증하고 XID wraparound 위험이 자랍니다. 만약 일시 정지가 필요해도 테이블 단위로만 + 명시적 복구 일정.정리
- VACUUM = dead 정리 + FSM/VM 갱신 + FREEZE
- autovacuum이 표준입니다. 운영자가 수동 호출은 ETL 직후 정도
- 큰 테이블은 scale_factor를 작게 (0.02~0.05)
- 큰 시스템은
autovacuum_vacuum_cost_limit을 키워 throughput ↑ - vacuum이 못 도는 원인은 거의 항상 긴 트랜잭션·잊혀진 prepared·idle slot
- VACUUM FULL은 운영 중 금지 — pg_repack 사용
다음 절(8.2)에서는 통계 갱신 — ANALYZE — 의 동작과 전략을 봅니다.