본문 바로가기

개발 지식/DB

Postgresql Vacuum

반응형

Postgresql Vacuum

Vacuum 이란

Postgresql은 다른 RDB와 달리, UPDATE 쿼리와 DELETE 쿼리가 순전히 기존 데이터를 변형하거나 삭제를 하는 방향으로 동작하지 않는다. 즉, UPDATE 쿼리는 기존에 있던 데이터는 그대로 둔 채 새로운 데이터를 추가하는 방식으로 동작하며, DELETE 쿼리 또한 기존의 데이터를 삭제하지 않고 삭제해야 한다는 표시만 남겨두는 방식으로 동작을 한다. 이는 Postgresql이 동시접근에 의한 이슈들을 방지하기 위해 채택된 MVCC 모델을 적용하고 있기 때문이다.

  • MVCC : 동시접근을 허용하는 데이터베이스에서 동시성을 제어하기 위해 사용하는 방법. 즉, MVCC 모델에서 데이터에 접근하는 사용자는 접근한 시점에서의 데이터베이스의 Snapshot을 읽는데, 이 snapshot 데이터에 대한 변경이 완료될 때(트랜잭션이 commit 될 때)까지 만들어진 변경사항은 다른 데이터베이스 사용자가 볼 수 없다. 이러한 개념에 의해 사용자가 데이터를 업데이트하면 이전의 데이터를 덮어 씌우는게 아니라 새로운 버전의 데이터를 생성한다. 대신 이전 버전의 데이터와 비교해서 변경된 내용을 기록한다. 이렇게해서 하나의 데이터에 대해 여러 버전의 데이터가 존재하게 되고, 사용자는 마지막 버전의 데이터를 읽게 된다.

위와 같이 삭제되거나 업데이트 처리가 된 기존 데이터들을 dummy 데이터라고 할 수 있는데, 이러한 dummy 데이터들을 clean 시켜주는 것이 vacuum 의 개념이라고 할 수 있다.

Vacuum의 목적

위에서 설명했듯이, Postgresql 에서는 DELETE 쿼리 혹은 UPDATE 쿼리시에 기존 데이터들이 삭제 되지 않는다. 따라서 이를 삭제 처리하고 디스크 용량을 유지 및 감소시키는 것이 Vacuum의 주 목적이라고 할 수 있다. 그리고 이러한 vacuum 처리를 직접 쿼리를 날리는 수동 vacuum이 있고, 테이블 및 DB에 설정을 추가하여 자동으로 vacuum 하는 방법이 있다.

Vacuum 이 주기적으로 되지 않는다면?

Vacuum 을 주기적으로 해주지 않으면 다음과 같은 문제가 발생된다고 azure 측에 명시되어 있다. (Azure Database for PostgreSQL)

  1. Data bloat, such as larger databases and tables. (데이터베이스와 테이블들이 커지는 데이터 팽창)
  2. Larger suboptimal indexes. (불필요한 혹은 부적절한 인덱스 증가)
  3. Increased I/O. (I/O 증가)

수동 Vacuum

-- DB 전체 full vacuum
vacuum full analyze;

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

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

-- 특정 테이블만 full vacuum
vacuum full [테이블 명];
  • full 옵션으로 실행시 데이터베이스에 lock이 걸리므로 운영중인 데이터베이스에서는 해당 옵션의 사용을 권장하지 않고 있다.

Autovacuum

Autovacuum 은 말 그대로 vacuum 을 자동으로 처리해주는 것이다. 따라서 수동 Vacuum처럼 명령어로 테이블을 정리하는 것이 아닌, 테이블 혹은 DB 단위의 설정을 통해서 vacuum이 진행된다. 그리고 이러한 설정값이 어떻게 되느냐에 따라 위에서 설명한 dead tuple(dummy 데이터)의 증가를 얼마나 억제시킬 수 있을지가 정해지기 때문에 Autovacuum 최적화에 대해서도 내용을 파악하고 있어야 한다.

Autovacuum 최적화 방법

  1. autovacuum_vacuum_scale_factor = 0 으로 설정

    • Autovacuum 을 최적화하는 가장 간단한 방법은 autovacuum_vacuum_scale_factor 를 0으로 설정하는 것이다. 이렇게 설정하게 되면 autovacuum_vacuum_threshold 에 지정된 숫자만큼의 dead tuple 에 따라 Autovacuum 이 동작하게 되므로 훨씬 일관성 있는 성능을 확보할 수 있다. 하지만 이 설정을 postgresql.conf (설정파일) 에 적용할 경우 모든 테이블에 이 설정이 적용되기 때문에 아래 예시 쿼리처럼 테이블별 설정을 통해 효율적으로 vacuum 이 동작하도록 해야한다.

      ALTER TABLE sample_table SET (autovacuum_vacuum_sacle_factor = 0.0);
      ALTER TABlE sample_table SET (autovacuum_vacuum_threshold = 100000);
    • autovacuum_vacuum_scale_factor = 0.0 : vacuum 작업의 트리거 여부를 결정할 때 autovacuum_vacuum_threshold 에 추가할 테이블 크기의 비율을 지정하는 값이다. 기본값은 0.2로, 테이블 크기의 20%를 의미한다

    • 즉, 위 설정이 적용되면 dead tuple 이 100,000 개가 생성될 때마다 Autovacuum이 동작하게 된다. 해당 설정은 운용중인 데이터베이스에서 진행해도 문제가 없다

  2. autovacuum_vacuum_cost_limit 증가

    • postgresql.conf 파일에는 autovacuum_vacuum_cost_limit 이라는 설정이 있다. 이는 다른 설정들과 함께 복합적으로 동작하며, Autovacuum이 한번 동작할 때의 동작 시간을 결정하게 된다. 이와 관련된 기본 설정은 아래와 같다.

      vacuum_cost_delay = 0
      vacuum_cost_page_hit = 1
      vacuum_cost_page_miss = 10
      vacuum_cost_page_dirty = 20
      vacuum_cost_lmit = 200
      
      autovacuum_vacuum_cost_limit = -1
      • autovacuum_vacuum_cost_limit이 -1일 경우, 해당 값은 vacuum_cost_limit을 참조한다.
      • Autovacuum이 한번 실행 될 때, 해당 프로세스는 200의 credit을 가진다.
      • page_hit 영역 (shared buffer 영역) 에 있는 데이터를 vacuuming 할 때마다 1의 credit을 소모한다.
      • page_miss 영역(디스크영역)에 있는 데이터를 vacuuming 할 때마다 10의 credit 을 소모한다.
      • page_dirty 영역에 있는 데이터를 vacuuming 할 때마다 20의 credit을 소모한다.
      • 200의 credit 이 모두 소진되면 해당 Autovacuum 프로세스는 종료된다.
    • 만일 테이블에 dead tuple 이 빈번하게 생성되는 경우, Autovacuum 이 한 번 수행될 때 조금 더 오랫동안(혹은 많이) 동작하도록 조정할 필요가 있따. 여기서 vacuum_cost_page_ 로 시작하는 설정들의 값을 낮추는 방법도 있지만, 해당 설정들은 전역적으로 적용되는 값이기 때문에 테이블 별로 별도 설정이 가능한 autovacuum_vacuum_cost_limit 값을 변경하는 편이 낫다. 아래 쿼리의 경우 vacuum credit을 1,000으로 조정하는 예제로, 기본 설정보다 약 5배 많은 vacuum을 한번에 처리하게 된다. 이 설정 역시 운용중인 데이터베이스에서 진행해도 문제가 없다.

      ALTER TABLE sample_table SET (autovacuum_vacuum_cost_limit = 1000);
  1. autovacuum_analyze_scale_factor 을 테이블 별로 별도 설정하기

    • PostgreSQL 은 Autovacuum 데몬을 통해 주기적으로 분석 데이터를 수집한다. 이 분석 데이터를 기반으로 해당 테이블에 select 쿼리를 수행할 때의 최적의 실행 계획을 수립하게 되는데, 이 역시 dead tuple의 존재가 분석 데이터에 좋지 않은 영향을 끼칠 수 있으므로 가능하면 autovacuum_vacuum_scale_factorautovacuum_vacuum_threshold 와 동일한 값으로 설정해 주도록 한다. 이 값 역시 테이블 별로 설정이 가능하며, 운용중인 데이터베이스에서 진행해도 문제 없다.

      ALTER TABLE sample_table SET (autovacuum_analyze_scale_factor = 0.0);
      ALTER TABLE sample_table SET (autovacuum_analyzer_threshold = 100000);
  2. autovacuum_work_mem, autovacuum_max_workers 의 최적화

    • Autovacuum 이 동작할 때, autovacuum_work_mem 에 설정된 메모리를 이용하게 된다 (해당 값이 -1일 경우, maintenance_work_mem 을 공유한다). 따라서 적절한 maintenance_work_mem 을 설정할 필요가 있는데, 해당 서버에서 오로지 PostgreSQL 만 동작한다고 가정할 경우, 적절한 maintenance_work_mem 의 값은 서버 메모리 1GB당 50MB를 할당하는 것이 일반적이지만 절대적이지는 않다.
    • autovacuum_max_workers 는 동시에 동작 가능한 Autovacuum 의 프로세스 갯수를 정의한다. Autovacuum 이 관리해야 할 테이블이 많다면 해당 값을 늘려야 한다. 늘리지 않을 경우 XID Freeze 가 제때 실행이 되지 않을 수 있으며 이는 치명적인 결과로 이어질 수 있다. autovacuum_max_workers 는 변경시 PostgreSQL 서버의 재시작을 필요로하므로 Autovacuum 의 동작을 꾸준히 모니터링하며 신중히 변경하는 것이 좋다.

Vacuum과 Autovacuum의 차이

수동 Vacuum과 Autovacuum의 가장 큰 차이점은 다음 2가지로 요약을 할 수 있을 것 같다.

  1. 해당 테이블 혹은 DB에 lock을 유발하는지에 대한 여부
    • DB lock 유발 : 수동 vacuum
    • 운영중인 DB에 영향 없음 : Auto vacuum
  2. 실제 디스크 저장 가능 용량이 줄어드는지에 대한 여부
    • 디스크 저장 가능 용량 줄어듦 : 수동 vacuum
    • 디스크 저장 가능 용량 줄어들지 않음 : Autovacuum

dead tuple 식별용 샘플 쿼리

SELECT relname, n_dead_tup, n_live_tup, (ndead_tup / n_live_tup) AS DeadTuplesRatio, last_vacuum, last_autovacuum 
FROM pg_catalog.pg_stat_all_tables 
WHERE relname = 'sample_table'  -- 본인의 테이블명
ORDER BY n_dead_tup DESC;


Reference

반응형