일요일

PostgreSQL VACUUM: 자동 실행과 수동 튜닝으로 성능 회복

🔍 검색 키워드: PostgreSQL VACUUM, 데드 튜플 정리, PostgreSQL 성능 저하, autovacuum, PostgreSQL 디스크 사용량, VACUUM ANALYZE

PostgreSQL은 MVCC(Multi-Version Concurrency Control) 방식으로 동작하기 때문에, 데이터를 삭제해도 물리적으로 즉시 제거되지 않습니다. 시간이 지나면서 불필요한 "죽은" 데이터(데드 튜플)가 누적되어 디스크 용량을 낭비하고 쿼리 성능을 저하시킵니다. 이 글에서는 VACUUM의 동작 원리와 최적화 방법을 설명합니다.

증상: PostgreSQL의 숨은 성능 저하

PostgreSQL 서버를 운영하다 보면 나타나는 문제들:

# 문제 1: 디스크 사용량은 계속 증가하지만 DELETE했는데도?
SELECT pg_size_pretty(pg_total_relation_size('users'));
-- 5GB

# 문제 2: 같은 쿼리인데 시간이 점점 느려짐
EXPLAIN ANALYZE SELECT * FROM orders WHERE status = 'completed';
-- Seq Scan on orders (cost=0.00..50000.00 rows=100000)

# 문제 3: 특정 테이블에서만 SELECT 느림
SELECT n_live_tup, n_dead_tup FROM pg_stat_user_tables
WHERE relname = 'orders';

-- 결과: n_live_tup=100000, n_dead_tup=500000 (데드 튜플이 많음!)

원인 분석: MVCC와 데드 튜플

PostgreSQL의 MVCC 동작 방식:

1. 트랜잭션 A가 행을 삭제하면, 실제로는 그 행을 "삭제됨" 표시
2. 트랜잭션 B가 여전히 이전 버전을 참조 중이면, 그 행은 메모리에 남아야 함
3. 모든 트랜잭션이 새 버전만 참조하게 되면, 그때 "데드 튜플" 처리 가능

데드 튜플이 문제인 이유:

- 디스크 용량 낭비: 삭제한 데이터도 차지
- 인덱스 비대화: 데드 튜플도 인덱스 스캔에 포함
- 쿼리 성능 저하: Seq Scan 비용 증가

원인 추적: 데드 튜플 상태 확인

데드 튜플 누적 상태를 확인합니다:

SELECT schemaname, relname, n_live_tup, n_dead_tup, 
       ROUND(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_ratio
       FROM pg_stat_user_tables
       ORDER BY n_dead_tup DESC
       LIMIT 10;

결과 해석:

- n_live_tup: 활성 행 수
- n_dead_tup: 데드 튜플 수
- dead_ratio: 데드 튜플 비율. 10% 이상이면 VACUUM 필요

해결방법 1: 자동 VACUUM (autovacuum) 설정 최적화

PostgreSQL은 기본적으로 autovacuum 데몬이 자동으로 VACUUM을 실행합니다. 하지만 기본 설정은 보수적이므로, 대용량 데이터베이스에서는 수동 튜닝이 필요합니다.

postgresql.conf 설정 확인:

SHOW autovacuum;
SHOW autovacuum_naptime;
SHOW autovacuum_vacuum_threshold;

권장 설정 (운영 환경):

postgresql.conf에 추가:

autovacuum = on
autovacuum_naptime = '10s'        # 10초마다 체크 (기본 1분)
autovacuum_vacuum_threshold = 50  # 50개 행 변경 시 VACUUM (기본 50)
autovacuum_analyze_threshold = 50 # 50개 행 변경 시 ANALYZE (기본 50)

# 테이블별 추가 튜닝
autovacuum_vacuum_scale_factor = 0.05  # 테이블의 5% 변경 시
autovacuum_vacuum_cost_limit = 1000    # I/O 부하 제한 (기본 -1)

재시작:

sudo systemctl restart postgresql

해결방법 2: 수동 VACUUM 실행

긴급 상황에서는 수동으로 VACUUM을 실행합니다:

# 기본 VACUUM (먹다 남은 공간 재사용)
VACUUM;

# ANALYZE 포함 (통계 업데이트)
VACUUM ANALYZE;

# 특정 테이블만 처리
VACUUM ANALYZE orders;

# FULL (여유 공간을 OS로 반환, 시간 오래 걸림)
VACUUM FULL;

주의: VACUUM FULL은 테이블을 잠금(LOCK)으로 처리하므로, 운영 중에는 피해야 합니다.

해결방법 3: 추가 최적화

1. REINDEX로 인덱스 재구성

데드 튜플로 비대화된 인덱스를 재구성:

REINDEX TABLE orders;

2. pg_repack 확장 모듈 (다운타임 없음)

CREATE EXTENSION pg_repack;
SELECT pg_repack.repack_table('orders');

pg_repack은 FULL VACUUM과 달리 테이블 잠금 없이 재구성합니다. 운영 중 사용 가능.

3. 정기적인 VACUUM ANALYZE 스케줄

크론 작업으로 자동 실행:

# 매일 새벽 2시에 VACUUM ANALYZE 실행
0 2 * * * psql -d mydb -c "VACUUM ANALYZE;"

성능 회복 비교

상황실행 명령어효과소요 시간
경미한 데드 튜플VACUUM ANALYZE통계 업데이트, 쿼리 최적화~초 단위
중간 정도 비대화REINDEX TABLE인덱스 재구성~분 단위
심각한 비대화pg_repack (비운영) 또는 VACUUM FULL공간 회복~분~시간 단위

모니터링 체크리스트

항목확인 방법권장 조치
데드 튜플 비율pg_stat_user_tables에서 dead_ratio10% 이상이면 VACUUM
자동 VACUUM 실행 여부pg_stat_user_tables의 last_vacuum안 실행되면 autovacuum_naptime 단축
인덱스 비대화pg_indexes_size(table)인덱스가 테이블보다 크면 REINDEX
디스크 사용량pg_size_pretty(pg_database_size(...))지속 증가면 VACUUM FULL 또는 pg_repack

PostgreSQL의 성능 저하는 대부분 데드 튜플 누적으로 인한 것입니다. 정기적인 VACUUM과 ANALYZE로 데이터베이스를 "청소"하면, 쿼리 성능과 디스크 효율을 동시에 개선할 수 있습니다.

댓글 없음:

댓글 쓰기