PostgreSQL: 베큠(VACUUM)을 실행해야되는 이유 그리고 성능 향상

간단 설명

베큠(Vacuum)은 PostgreSQL의 쓰레기 데이터를 정리하여 쾌적하게 청소하라는 명령인데, 쉽게 "디스크 조각 모음"이라고 생각하면된다.

베큠을 해야되는 이유

데이터는 물리적으로 디스크에 저장되고 읽어서 보여주는데, 데이터를 갱신(UPDATE) 혹은 삭제(DELETE) 시에 디스크에 있던 기존 정보를 갱신하거나 삭제하지 않습니다.
기존 정보는 변경되었다는 표시를 남기고 새롭게 디스크에 갱신(UPDATE)된 정보를 기록합니다.
삭제(DELETE) 했어도 디스크 용량은 줄어들지 않으며 갱신(UPDATE) 시에는 새로운 행이 추가되기 때문에 디스크 용량이 증가하게되는거죠.

이런 개념은 MVCC(다중 버전 동시성 제어) 구현에 따른 튜플(Tuple) 개념때문에 그렇습니다.
다시 쉽게 말하면 트랜잭션(Transaction)을 사용하기 위해서 기존 데이터를 변경하지 않고 보관하게 된거라고 생각하면됩니다.
즉 그 말은 UPDATE, DELETE, Transaction 이벤트가 많아질 수록 데드 튜플(Dead Tuple) 발생에 따른 디스크 I/O(Disk I/O) 증가가 성능 저하라는 결과를 가져오게됩니다.

베큠 기대 효과

PostgreSQL의 모든 정보는 Pg_Catelog에 쌓이고 있는데, 튜플에 대한 정보도 가지고 있으며 그 정보를 기반으로 쿼리 플랜(Query Plan)에도 활용하고 있습니다.
잘 정리된 책상에서 쉽게 물건을 찾을 수 있듯 주기적인 베큠 실행은 성능 향상을 가져옵니다.

베큠 명령어 예제

주의: full 옵션으로 실행 시 데이터베이스가 잠김(Lock)처리가 되므로 운영중인 데이터베이스에서는 해당 옵션으로 사용하지 마세요.
-- DB 전체 풀 실행
vacuum full analyze;

-- DB 전체 간단하게 실행
vacuum verbose analyze;

-- 해당 테이블만 간단하게 실행
vacuum analyse [테이블 명];

-- 특정 테이블만 풀 실행
vacuum full [테이블명];

베큠이 청소하는 기준

사람마다 청소하는 방범이 있듯 PostgreSQL도 청소 기준을 가지고 있습니다.
그 기준을 FSM(Free Space Map)이라고하는데, 더 이상 필요하지 않는 행의 정보를 보유하고 있습니다.
이 정보는 실제로 사용되지는 않지만 용량을 차지하고 있고, 새로운 행이 삽입될때 DBMS는 FSM의 여유 공간을 확인하여 해당 행을 사용하게 됩니다.
그리고 FSM 공간은 용량이 제한되어 있기때문에 주기적으로 청소하는게 좋습니다.

아래 쿼리는 튜플(Tuple)에 대한 정보를 확인 할 수 있는 쿼리문입니다.
SELECT
    n.nspname AS schema_name,
    c.relname AS table_name,
    pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid) as total_tuple,
    pg_stat_get_live_tuples(c.oid) AS live_tuple,
    pg_stat_get_dead_tuples(c.oid) AS dead_tupple,
    round(100*pg_stat_get_live_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as live_tuple_rate,
    round(100*pg_stat_get_dead_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as dead_tuple_rate,
    pg_size_pretty(pg_total_relation_size(c.oid)) as total_relation_size,
    pg_size_pretty(pg_relation_size(c.oid)) as relation_size
FROM pg_class AS c
JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace 
WHERE pg_stat_get_live_tuples(c.oid) > 0
AND c.relname NOT LIKE 'pg_%'
ORDER BY dead_tupple DESC;

아래 쿼리는 베큠 통계 정보를 확인 할 수 있는 쿼리문이며, VACUUM ANALYZE 명령어 실행 시 갱신됩니다.
SELECT * FROM pg_stat_all_tables ORDER BY schemaname, relname;

참고

vacuum full 실행 시에는 pg_classrelfilenode값이 변경됩니다.
relfilenode 값은 디스크에 저장되는 파일명과 동일하며 아래 쿼리로 물리적인 파일의 위치를 찾을 수 있습니다.
SELECT oid, pg_relation_filepath(oid), relname, relfilenode FROM pg_class LIMIT 10;

함께보기