데이터베이스 기술/POSTGRESQL

PostgreSQL - Vacuum의 A to the Z

(주)비트나인 2023. 8. 29. 11:49

1. Intro

PostgreSQL에서 Vacuum은 DB의 성능을 높이는 부분에 있어 매우 중요한 기능입니다. 또한 Vacuum은 MVCC를 구현하기위해 PostgreSQL에만 있는 개념이기에 그 차이점에 더 주목해야합니다.

우리는 Vacuum의 단어에서 짐작할 수 있듯이 DB를 ‘청소’를 한다는 의미와 일맥상통합니다. 더러워진 공간을 청소하여 새로운 공간을 만드는 것입니다. 한마디로 Vacuum은 디스크에서 불필요한 공간을 정리하여 용량을 성능을 높이는 작업을 말합니다. Vacuum에 대해 자세히 알기전에, DB의 MVCC개념을 먼저 이해해야 합니다.

2.MVCC의 중요성

DB에 많은 사용자들이 접근한다고 하였을 때, 사용자들은 각각 데이터를 변경하거나 조회를 합니다. 이때 많은 작업들이 동시에 이루어질 수가 있습니다. 실시간으로 변하고 있는 데이터에서 사용자는 조회한 시점에 정확한 데이터 정보를 확인하는 것이 중요합니다. 이러한 일관성을 유지하는 것을 MVCC (Multi-Version Concurrency Control) ”다중버전 동시성 제어”입니다.

MVCC는 DB에 동시접근을 제공하는 것을 말합니다. Lock을 걸지 않고 데이터의 읽기의 일관성을 보장합니다. 사용자는 DB에 접근한 시점에 데이터베이스의 스냅샷을 읽고 다른 사용자가 데이터를 변경할 때 데이터 변경이 완료가(트랜잭션 commit)되기 전까지 변경사항을 볼 수 없습니다.

MVCC를 구현하는 방식은 DBMS마다 다르지만 그 목적은 같습니다. 대표적으로 Oracle과 PostgreSQL의 방식을 살펴보겠습니다.

  • ORACLE의 MVCC “Rollback segment”

오라클에서는 MVCC구현 방식으로 Rollback segment를 사용합니다. 데이터를 조회한 시점에 트랜잭션에 의해 변경하는 작업이 진행되면, 기존 블록을 새로운 데이터로 변경하고, 기존 데이터는 Rollback segment에 저장합니다.

  • PostgreSQL의 MVCC “MGA”

PG에서는 MGA방식을 사용합니다. 특정데이터를 변경할 때 기존 Tuple을 그대로 두고 새로운 데이터를 추가하여 변경합니다. 이렇게 신규 Tuple을 같은 page에 저장하고 Tuple별 생성 시점과 변경 시점을 기록하여 비교하는 식으로 MVCC를 구현합니다. 그러면 기존 Tuple은 지워지지 않는데 용량만 차지하는 Dead Tuple이 되므로 vacuum을 해줘야 하는 것입니다.

또한, Tuple이 생성되거나 변경된 시점을 각 Tuple 내 xmin, xmax라는 메타데이터 Field에 기록하여 어떤 Tuple을 읽을 수 있는지 버전 관리합니다. 이때xmin은 Tuple을 insert, update하는 시점에 트랜잭션 ID를 갖는 메타데이터이고 xmax는Tuple을 delete하거나 update하는 시점의 트랜잭션 ID를 갖는 메타데이터입니다.

그렇다면 Vacuum은 MVCC를 구현하는데 있어 매우 중요한 작업입니다. 이제 Vacuum에 대해 자세히 알아보겠습니다.

3.Vacuum이란?

 

PostgreSQL에서 어떤 데이터를 Delete 한다고 했을 때, 데이터상으로는 지워지지만 디스크에는 바로 삭제되지 않습니다. 왜냐하면 삭제하려는 데이터를 다른 트랜잭션에서 사용될 수 있기 때문입니다.

그렇지만 더 이상 사용하지 않는다고 할 때는 스토리지 안에서 불필요한 공간을 차지하기 때문에 성능이 저하될 수 있습니다. 이러한 불필요한 공간을 정리하기위해 vacuum을 사용합니다.

즉, Vacuum은 PostgreSQL에만 있는 기능으로 MVCC동작원리에 따른 공간 비효율과 XID Wraparound발생 등 PostgreSQL특성상 발생하는 문제점을 해결하기 위해 나왔습니다. 이에 따른 Vacuum의 필요성이자 주요 4가지 역할에 대해 살펴보겠습니다.

1) Dead Tuple정리해서 FSM(Free Space Map)반환

우선 Dead Tuple은 commit이 완료된 이미 지워지거나 업데이트가 끝난 old한 데이터입니다. 또한 FSM은 디스크의 사용 가능한 공간을 표시한 지도 같은 것으로 사용할 수 있는 공간이 있는지 확인하고 없으면 확보를 추가적으로 합니다. Vacuum으로 Dead Tuple을 정리하여 FSM으로 반환했을 때 불필요한 Dead Tuple의 page를 읽지 않아도 되니 디스크I/O발생을 막을 수 있습니다.

2) Transaction ID Wraparound방지

트랜잭션 ID가 겹치게 되면 과거 데이터가 손실될 수도 있습니다. 그래서 XID겹침을 방지하기 위해 주기적으로 Vacuum을해야합니다. 왜냐하면 데이터 손실로 서비스 중단까지 야기할 수 있는 문제이기에 주의해야 합니다. 특히 vacuum은 오래된 XID값을 fozenXID 값으로 바꿔서 겹칩 오류를 방지합니다.

3) visibility map을 갱신하여 index scan 성능 향상

Visibility Map은 테이블에서 실행 중인 트랜잭션이 참조하는 튜플만 포함하는 페이지를 추적하는 역할을 합니다. Vacuum은 이러한 페이지에 대해서는 정리할 수 없어서 해당 페이지에 대해 작업을 건너뛰지만, 테이블 참조 없이 인덱스만으로 쿼리에 응답할 수 있도록 index-only scan 합니다. PostgreSQL 현재 트랜잭션에 의해 참조되어야 하는 것인지 여부를 인덱스에 매칭되는 메모리 힙 튜플에서 가져오게 됩니다. 반면 index-only scan은 Visibility Map을 우선 체크하게 되어 디스크 스캔을 피할 수 있게 됩니다.

4) 통계정보 갱신

Pg_class에 담기는 테이블 정보와, 실시간 통계정보 파일의 내용을 갱신하여 보여줍니다.

-Vacuum의 실행 기준

Vacuum이 실행되기 위해서는 FSM의 기준을 따릅니다. FSM의 공간이 여유 있는지 확인하여 Vacuum을 실행해야 합니다.

  • Vacuum analyze 쿼리문

또한 Vacuum모니터링을 위한 통계정보로 테이블별 입력, 갱신, 삭제된 로우 수는 pg_stat_all_table에서 확인할 수 있고 dead tuple개수도 확인 가능합니다.

  • Tuple확인쿼리문

4.Vacuum활용방안&최적화

-Vacuum과 AutoVacuum

Vacuum은 직접 쿼리를 입력해서 작업하는 수동Vacuum과 자동으로 작업하는 Autovacuum이 있습니다.

  • Vacuum

직접 Vacuum쿼리문을 입력해서 필요할 때 작업을 진행할 수 있습니다.

하지만 운영중인 DB라면 DB lock이발생하여 적합하지 않습니다.

  • AutoVacuum

AutoVacuum은 한마디로 자동으로 vacuum작업을 수행하는 것입니다. AutoVacuum은 대표적으로 Dead Tuple의 누적이 임계치에 도달했을 때, Tuple의 age가 누적되어 임계치에 도달했을 때 수행됩니다. 또한 진행시 DB lock이 걸리지 않는다는 점이 Vacuum full과 큰 차이점입니다.

미리 설정한 값으로 AutoVacuum이 작동되는데 Dead tuple을 얼마나 정리할 수 있을지 정할 수 있기 때문에 최적화하는 방법도 중요합니다. AutoVacuum을 최적화하는, 대표적인 3가지의 설정 방법을 소개하고자 합니다.

1. autovacuum_vacuum_scale_factor 최적화

autovacuum_vacuum_scale_factor을 0으로 설정하면 autovacuum_vacuum_threshold에 설정한 숫자만큼의 dead tuple에 따라 동작하게 됩니다. 그렇기 때문에 안정적인 성능을 가져갈 수 있습니다.

2. autovacuum_vacuum_cost_limt 최적화

Autovacuum의 동작 시간을 결정하는 파라미터입니다.

  • autovacuum_vacuum_cost_limit = 200: AutoVacuum(Vacuum) 이 한 번 수행될 때 마다 해당 Vacuum 프로세스는 200의 credit을 가집니다.
  • vacuum_cost_delay = 0: AutoVacuum이 autovacuum_vacuum_cost_limit 만큼 완료되면 다음 AutoVacuum은 이 몇 밀리초 동안 sleep합니다.
  • vacuum_cost_page_hit = 1: page_hit (shared_buffer)에 있는 데이터를 Vacuum 할 때 마다 1의 credit을 소모합니다.
  • vacuum_cost_page_miss = 10: page_miss (디스크 영역)에 있는 데이터를 Vacuum할 때마다 10의 credit을 소모합니다.
  • vacuum_cost_page_dirty = 20: Dead Tuple을 Vacuum할 때마다 20의 credit을 소모합니다.

AutoVacuum을 오래 동작하도록 해야할 때 모든 테이블에 한번에 적용하는 파라미터 값도 있지만 테이블 별로 설정이 가능한 autovacuum_vacuum_cost_limit으로 최적화 하는것이 더 효율적입니다.

3. autovacuum_max_worker 최적화

Autovacuum_max_worker 값을 늘리면, 테이블중에서 AutoVacuum을 하지 못했을 때 효과적으로 처리할수 있습니다. 특히 파티셔닝 테이블의 경우 각각 AutoVacuum worker이 할당되어서 더 빠른 작업이 가능해집니다. 즉 Autovacuum_max_worker은 동시에 동작 가능한 AutoVacuum의 프로세스 개수를 정의합니다.

Vacuum을 활용하고 최적화하는 방법은 다양하지만, DB 서비스의 특성을 살펴보고 적합한 파라미터를 값을 찾아 튜닝하는 것이 중요합니다. 그렇기 때문에 우리는 파라미터 값을 지속적으로 모니터링하면서 최적의 튜닝 방법을 찾는 것이 관건입니다.

글 : 김선림 전임 ( 비트나인 DB Tech팀 )