숏컷 코드
VACUUM ANALYZE orders;문법
MVCC 모델은 UPDATE / DELETE 마다 죽은 튜플을 남겨 테이블을 부풀린다
PostgreSQL 은 행을 덮어쓰지 않습니다. UPDATE 는 기존 행을 "삭제된 것으로 표시"하고 새 버전을 추가하며, DELETE 도 행을 실제로 지우지 않고 만료 표시만 합니다. 오래된 스냅샷을 참조하는 트랜잭션이 없을 때 비로소 이 죽은 튜플(dead tuple)을 정리할 수 있습니다. 정리되지 않으면 테이블이 계속 커져 Sequential Scan 비용이 늘고, 인덱스 크기도 함께 부풀어 오릅니다.
-- 테이블별 죽은 튜플 수 확인
SELECT
relname,
n_live_tup,
n_dead_tup,
round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS dead_ratio_pct,
last_vacuum,
last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;VACUUM과 ANALYZE는 해결하는 문제가 다르다
VACUUM은 죽은 튜플을 정리해 공간을 재사용 가능하게 만들고, ANALYZE는 통계를 갱신해 옵티마이저가 더 나은 계획을 고르게 합니다. 느린 쿼리를 볼 때 둘을 같은 작업으로 보면 원인을 헷갈리기 쉽습니다.
VACUUM orders; -- 공간 재사용
ANALYZE orders; -- 통계 갱신
VACUUM ANALYZE orders; -- 둘 다 같이VACUUM 은 공간을 재사용 가능하게 만들고 VACUUM FULL 만 실제 공간을 반환한다
일반 VACUUM 은 죽은 튜플을 정리해 그 공간을 같은 테이블의 새 행이 재사용할 수 있도록 표시합니다. 그러나 운영체제에 반환하지는 않으므로 파일 크기 자체는 줄어들지 않습니다. VACUUM FULL 은 테이블을 새로 작성해 실제 디스크 공간을 반환하지만, 실행 중 테이블 전체에 배타적 잠금을 걸어 다른 쿼리가 완전히 차단됩니다. 운영 환경에서는 pg_repack 같은 도구로 잠금 없이 재팩킹하는 것이 일반적입니다.
-- 일반 VACUUM: 공간 재사용, 잠금 없음
VACUUM orders;
-- VACUUM ANALYZE: 정리와 통계 갱신 동시 수행
VACUUM ANALYZE orders;
-- VACUUM FULL: 디스크 반환, 테이블 잠금 주의
-- VACUUM FULL orders; -- 운영 중 사용 자제ANALYZE 가 없으면 옵티마이저가 잘못된 실행 계획을 선택한다
ANALYZE 는 컬럼별 데이터 분포(히스토그램, 가장 많은 값, NULL 비율 등)를 pg_statistic 에 저장합니다. 옵티마이저는 이 통계를 바탕으로 Sequential Scan 과 Index Scan 중 어느 쪽이 유리한지, 조인 방식(Nested Loop / Hash Join / Merge Join)은 무엇이 적합한지 판단합니다. 대규모 INSERT 나 배치 처리 후 통계가 현실을 반영하지 못하면 옵티마이저가 잘못된 계획을 고르고, 실제 수행 시간이 예상보다 훨씬 길어집니다.
-- 통계 갱신
ANALYZE orders;
-- 특정 컬럼만 통계 갱신
ANALYZE orders (status, created_at);
-- 통계 확인
SELECT attname, n_distinct, correlation
FROM pg_stats
WHERE tablename = 'orders';autovacuum 은 사실상 필수 안전장치이지만 대량 쓰기 직후에는 수동 보완이 필요하다
autovacuum 데몬은 테이블별 죽은 튜플 비율과 변경 행 수가 임계값을 넘으면 자동으로 VACUUM 과 ANALYZE 를 실행합니다. 기본 임계값(autovacuum_vacuum_threshold = 50, autovacuum_vacuum_scale_factor = 0.2)은 대용량 테이블에서는 너무 늦게 반응할 수 있습니다. 수백만 행 테이블에서는 scale_factor 를 낮춰 더 자주 실행되도록 조정하거나, 대량 배치 처리 직후에는 수동으로 VACUUM ANALYZE 를 실행하는 것이 좋습니다.
-- 특정 테이블에 대해 autovacuum 파라미터 조정
ALTER TABLE orders SET (
autovacuum_vacuum_scale_factor = 0.01,
autovacuum_analyze_scale_factor = 0.005
);
-- autovacuum 상태 모니터링
SELECT pid, datname, relid::regclass, phase, heap_blks_scanned, heap_blks_vacuumed
FROM pg_stat_progress_vacuum;VACUUM, ANALYZE, autovacuum은 트리거 시점이 다르다
즉시 공간 재사용이 필요하면 VACUUM, 계획 추정이 틀린 것 같으면 ANALYZE, 평소 자동 유지 관리는 autovacuum이 맡습니다. 셋을 전부 같은 "청소 명령"으로 보면 왜 느린지 원인을 잘못 짚기 쉽습니다.
VACUUM orders;
ANALYZE orders;
VACUUM ANALYZE orders;선택 기준
| 상황 | 적합한 선택 |
|---|---|
| 죽은 튜플 정리, 공간 재사용 | VACUUM 테이블명 |
| 정리와 통계 갱신을 함께 | VACUUM ANALYZE 테이블명 |
| 디스크 공간 실제 반환이 필요할 때 | VACUUM FULL (잠금 주의) 또는 pg_repack |
| 옵티마이저 계획이 비효율적일 때 | ANALYZE 로 통계 갱신 후 재확인 |
| 대용량 테이블에서 autovacuum 이 늦을 때 | autovacuum_vacuum_scale_factor 조정 |
주의할 점
느린 쿼리의 원인이 항상 인덱스 부족은 아닙니다. ANALYZE 가 오래돼 통계가 현실을 반영하지 못하면 옵티마이저가 잘못된 실행 계획을 선택합니다.
VACUUM FULL 은 실행 중 테이블 전체를 잠그므로 운영 환경에서 직접 사용하면 서비스 중단이 발생할 수 있습니다.
autovacuum 을 비활성화하면 트랜잭션 ID 소진(XID wraparound) 위험이 생겨 데이터베이스 전체가 중단될 수 있습니다.
VACUUM FULL orders;운영 중 이 명령을 무심코 실행하면 테이블 전체 잠금 때문에 서비스가 멈춘 것처럼 보일 수 있습니다. 공간 회수가 정말 필요한지, 락 허용 범위인지 먼저 판단해야 합니다.
-- 대량 적재 직후
COPY orders FROM STDIN CSV;
-- 바로 서비스 트래픽 재개대량 적재나 대규모 UPDATE 직후에 autovacuum이 곧 알아서 처리하겠지 하고 기다리면, 한동안 통계가 낡은 상태로 실행 계획이 흔들릴 수 있습니다. 이런 작업 직후에는 수동 ANALYZE 또는 VACUUM ANALYZE를 먼저 검토하는 편이 안전합니다.
참고 링크
3 sources