Vacuum 과 Autovacuum 적용 방안이 있는가?
개요
Vacuum 의 대상을 판단하는 방법이나 파라미터 조정을 통해 최대한 Vacuum 의 부하를 덜어낼 수 있는 방법에 대해 확인해보겠습니다.
Vacuum
MVCC 모델링에 따라 공간 비효율과 XID Wraparound 발생 등 문제점을 보완하기 위해 만들어진 Vacuum 에 대해 알아보겠습니다.
우선, 데이터베이스 관리자가 메뉴얼하게 수행할 수 있는 VACCUM, VACUUM FULL, VACUUM FREEZE 등으로 명령어 조합에 따라 사용할 수 있습니다.
- Vacuum의 주요 목적
- 디스크 공간 확보
- Transaction ID Wraparound 로 인한 오래된 데이터의 손실 방지
- 통계 정보 갱신
- Visibility Map (VM) 갱신 작업
PostgreSQL 은 Dead Tuple 을 삭제하지 않고 남겨놓기 때문에 테이블의 사이즈는 무한정 증가할 수 밖에 없습니다.
Vacuum 은 이처럼 구 버전 데이터를 재사용 가능한 상태로 변경하거나, 구 버전 데이터의 물리적인 공간을 OS 로 반환함으로써 디스크 공간을 확보하는 역할을 수행합니다.
전자의 경우 Standard Vacuum 이라고 부르며, VACUUM 명령어에 의해 수행됩니다. 반면 후자는 구 버전 데이터를 제외하여 파일을 재구성하는 방식으로 VACUUM FULL 명령어에 의해 수행됩니다.
Standard Vacuum 수행으로 인한 재사용 공간의 확보는 데이터 증가를 억제하는 효과가 있기 때문에 최적의 공간 유지라는 일부 목적은 달성할 수 있습니다.
반면, VACUUM FULL 작업의 경우 앞서 말한 Standard Vacuum 과는 전혀 다른 방식으로 동작합니다.
Standard Vacuum 이 기존 데이터에 대해 재사용 가능한 공간을 확보하고 반환하는 과정이라 한다면, VACUUM FULL 은 일종의 Table Reorganization 작업과 유사하다 할 수 있습니다.
즉, Live Tuple 만 모아서 새로운 테이블을 구성하고, 기존 테이블을 삭제하는 과정을 통해 물리적인 공간 반환이 가능합니다.
하지만, 이러한 재구성 방식 동작 과정은 Table 에 대한 Exclusive Lock 획득이 필요하며, 동시성을 떨어뜨리는 원인이 되기 때문에 주의가 필요합니다.
또한, 신규 테이블을 위한 일정량의 디스크 공간이 존재해야 작업을 시작할 수 있습니다. 필요한 디스크 공간은 대상 테이블의 사이즈 만큼 필요합니다.
Autovacuum
Autovacuum 이란 Vacuum 수행을 자동화해주는 기능입니다. Autovacuum 기능은 Autovacuum Daemon 이라는 프로세스들에 의해서 수행되며, Autovacuum Launcher 프로세스는 Worker 프로세스를 관리하고 지시하는 역할을 수행합니다.
Autovacuum 이 동작하기 위해서는 PostgreSQL 파라미터 중 autovacuum 과 track_counts 파라미터가 켜져 있어야 합니다.
이때 Autovacuum Launcher 프로세스는 autovacuum_naptime (sec) 값으로 지정한 초 간격으로 한 번에 하나의 데이터베이스를 작업할 수 있도록 Worker 프로세스의 실행 시간을 관리합니다.
즉, N 개의 데이터베이스가 있다면 autovacuum_naptime / N 초 간격으로 Worker 프로세스는 자신이 작업 해야할 데이터베이스를 대상으로 작업을 시작합니다.
또한, 최대 autovacuum_max_workers 개수 만큼의 Worker 프로세스를 생성하여 Vacuum 작업을 수행하게 됩니다.
Parameter
|
Default Value
|
autovacuum
|
on
|
track_counts
|
on
|
autovacuum_naptime
|
60
|
autovacuum_max_workers
|
3
|
*Database Cluster 내에 한 개 이상(N)의 데이터베이스를 운영할 경우 동작 주기와 프로세스가 N개로 분산될 수 있습니다. 데이터베이스를 추가하는 경우 초기 관련 파라미터의 증가도 고려해야 합니다.
아래와 같이 pg_stat_all_tables.n_tup_xxx 값들이 아래 공식에 의해 계산된 수치보다 높은 경우에 Autovacuum 의 대상으로 선정됩니다.

Parameter
|
Default Value
|
autovacuum_vacuum_scale_factor
|
0.2
|
autovacuum_vacuum_threshold
|
50
|
autovacuum_vacuum_insert_scale_factor
|
0.2
|
autovacuum_vacuum_insert_threshold
|
1000
|
autovacuum_analyze_scale_factor
|
0.1
|
autovacuum_analyze_threshold
|
50
|
- Autovacuum 의 동작 여부를 결정하는 인자 중 autovacuum_vacuum_scale_factor 는 특정 테이블의 Live Tuple 대비 Dead Tuple 의 비율을 계산합니다.
-
비율 기반으로 Autovacuum 이 동작하기 때문에 테이블의 전체 튜플이 커질수록 처리해야 할 Dead Tuple 의 수가 비율적으로 급증하게 됩니다. 만일 10만 건의 튜플을 가진 테이블이라면 2만 개이상의 Dead Tuple 이 생성될 때 Autovacuum 이 동작하지만, 1억 건의 레코드를 가진 테이블이라면 2천만 건의 Dead Tuple 이 생성되어야 Autovacuum 이 동작하게 됩니다.
즉, 튜플이 많아질수록 Autovacuum 의 동작 주기는 점점 길어짐과 동시에 한 번에 처리해야 할 Dead Tuple 도 많아집니다. - 한번에 처리해야할 Dead Tuple 의 수가 증가하게 될 경우, 그에 맞추어 다양한 추가적인 설정의 최적화를 필요로 합니다.
Vacuum 을 위한 테이블 모니터링
지금까지 임계치 계산식이나, 필요한 데이터들을 pg_stat_all_tables 에서 확인해보았는데, Analyze 가 수행되고 나면, 테이블에 대한 정보들을 해당 View 들을 통해 조회할 수 있습니다.

현재 PostgreSQL 테이블에 있는 Dead Tuple / Live Tuple 비율을 확인해보거나 자동으로 수행된 Vacuum 이나 Analyze 수행 시각을 확인할 수 도 있습니다.

적절히 쿼리를 작성하여 Dead Tuple 생성치를 Autovacuum 이 적절히 정리하고 있는지 확인할 수도 있습니다.
Autovacuum 최적화
Autovacuum 을 설정하는데 가장 중요한 것은 적절한 빈도로 수행되도록 하는 것입니다.
보통 Vacuum 의 동작은 비용이나 부하가 많이 필요한 작업이기 때문에 Dead Tuple 정리와 Vacuum 의 부하 간 적절한 균형을 찾는 것이 중요합니다.
지금부터 Autovacuum 의 설정을 살펴보면서 Dead Tuple 의 증가를 억제할 수 있는지 알아보겠습니다.
1. autovacuum_vacuum_scale_factor 를 0으로 설정
Autovacuum 최적화 하는 가장 간단한 방법은 autovacuum_vacuum_scale_factor 를 0 으로 설정하는 것입니다. 이렇게 설정하게 되면 autovacuum_vacuum_threshold 에 지정된 숫자만큼의 Dead Tuple 에 따라서 Autovacuum 동작이 수행하므로 일관성 있는 성능을 확보할 수 있습니다.
해당 설정을 postgresql.conf 파일에 적용할 경우 모든 테이블에 영향을 주기 때문에, 아래와 같이 테이블 별로 설정할 수도 있습니다.
설정을 마친 후에는 해당 테이블은 Dead Tuple 이 10000개가 생성될 때 마다 Autovacuum 이 동작하게 됩니다. 해당 설정은 운영 중인 데이터베이스에서 설정해도 문제될 것이 없습니다.

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

autovacuum_vacuum_cost_limit : Autovacuum 이 한 번 수행될 때 마다 해당 Vacuum 프로세스는 200 의 credit 을 가집니다.
Parameter | Description |
---|---|
autovacuum_vacuum_cost_limit | -1 일 경우, 해당 값은 vacuum_cost_limit 값을 참조합니다. 따라서 Autovacuum 이 한 번 수행될 때 마다 해당 Vacuum 프로세스는 200의 credit 을 가집니다. |
vacuum_cost_delay | Autovacuum이 autovacuum_vacuum_cost_limit 만큼 완료되면 다음 AutoVacuum은 해당 몇 밀리초 동안 sleep합니다. |
vacuum_cost_page_hit | page_hit 영역(shared buffer 영역)에 있는 데이터를 vacuuming 할 때 마다 1 의 credit 을 소모합니다. |
vacuum_cost_page_miss | page_miss 영역(디스크 영역)에 있는 데이터를 vacuuming 할 때 마다 2 의 credit 을 소모합니다. |
vacuum_cost_page_dirty | Dead Tuple 을 vacuuming 할 때 마다 20 의 credit 을 소모합니다. |
vacuum_cost_limit | 200 의 credit 이 모두 소진되면 해당 Autovacuum 프로세스는 종료됩니다. |
autovacuum_vacuum_cost_limit 값이 -1 로 설정되었다면, vacuum_cost_limit 값에 의해 주어진 200의 credit 이 모두 소진되며 해당 Autovacuum 프로세스는 종료됩니다.
그렇기 때문에 해당 값이 너무 작으면 Autovacuum 이 Dead Tuple 을 충분히 다 정리하지 못한 채 끝나버려서 Dead Tuple 이 계속 누적되는 경우가 생길 수 있습니다.
만약 테이블에 Dead Tuple 이 빈번하게 높은 수준으로 생성될 경우 우리는 Autovacuum 이 한 번 수행될 때 조금 더 오랫동안 혹은 많이 동작하도록 해야 할 필요가 있습니다.
여기서 vacuum_cost_page_ XXX 로 시작하는 설정들의 값을 낮추는 방법도 있지만, 해당 값들은 전역적으로 적용되는 값들이기 때문에 테이블 별로 설정이 가능한 autovacuum_vacuum_cost_limit 값을 변경하는 것을 추천합니다.
다음 예제 쿼리는 특정 테이블의 vacuum credit 을 1,000 으로 상향 조정하는 예제입니다. 이렇게 설정할 경우 기본 설정보다 약 5배 많은 vacumming 을 한 번에 처리하게 됩니다.
해당 설정 역시 운용 중인 데이터베이스에서 설정해도 문제가 없습니다.

3. autovacuum_analyze_scale_factor 을 테이블 별로 설정하기
PostgreSQL 은 Autovacuum 데몬을 통해 주기적으로 분석 데이터를 수집합니다. 해당 데이터를 기반으로 테이블에 Select 쿼리를 수행할 때의 최적의 실행 계획을 수립하게 됩니다.
이 역시 Dead Tuple 의 존재가 분석 데이터에 좋지 않은 영향을 끼칠 수 있으므로 가능하다면 아래 파라미터를 설정해줍니다.
해당 값 역시 테이블 별로 설정할 수 있으며, 운용 중인 데이터베이스에서 설정해도 문제가 없습니다.

4. autovacuum_work_mem, autovacuum_max_workers 의 최적화
Autovacuum 이 동작할 때 autovacuum_work_mem 에 설정된 메모리를 이용하게 됩니다. (해당 값이 -1 일 경우, maintenance_work_mem 을 공유합니다.) 따라서 적절한 maintenance_work_mem 을 설정할 필요가 있습니다. 일반적으로 RAM 1GB 당 50MB 를 maintenance_work_mem 에 할당합니다.
현재는 System Memory * 0.15 / 3(autovacuum_max_workers) * 1024 와 같은 식으로 maintenance_work_mem 을 설정하도록 하고 있습니다.
따라서, Autovacuum 의 효율을 늘리고 싶다면 maintenance_work_mem 과 별도로 autovacuum_work_mem 파라미터를 설정할 수도 있습니다.
5. autovacuum_max_workers 의 최적화
autovacuum_max_workers 는 동시에 동작 가능한 Autovacuum 의 프로세스 개수를 정의합니다. 기본값으로 3으로 설정되어 있습니다.
Autovacuum 이 관리할 테이블이 많다면 해당 값을 늘려야합니다. 늘리지 않을 경우 XID Freeze 가 제때 실행되지 않을 수 있으며 이는 치명적인 결과로 이어질 수 있습니다.
autovacuum_max_workers 변경 시 PostgreSQL 서버 재시작이 필요하므로 Autovacuum 의 동작을 꾸준히 모니터링하며 신중히 변경하는 것이 좋습니다.
해당 파라미터를 2배로 늘린다고 Autovacuum 의 처리량과 성능이 2배가 되는 것은 아닙니다.
만약 아주 큰 테이블을 vacuuming 하는 도중 autovacuum worker 가 모두 사용 중이고 작은 테이블들을 vacuum 하지 못하는 현상이 발생했을 때, 해당 값을 늘리면 효과적일 수 있습니다.
서버의 CPU 코어 수를 따져서 너무 과하게 세팅하지 않도록 합니다. (전체 CPU core 수 / 2, 3 정도로)
6. autovacuum_naptime 설정
Autovacuum 프로세스는 autovacuum_naptime 마다 각 데이터베이스에서 worker 를 수행하려고 시도합니다. 기본값으로 60 초로 설정되어 있습니다.
예를 들어 10개의 데이터베이스가 있고, autovacuum_naptime 이 60초로 설정된 경우 (60/10) 초 마다 autovacuum 을 수행하려고 합니다.
지금까지 Vacuum 의 내용과 Autovacuum 의 내용을 확인해보았습니다.
마지막으로 정리를 조금 덧붙이자면 Vacuum 이나 Autovacuum 구성 파라미터 같은 경우 변경하는 방법은 간단하나 적용을 위해 고려해야 하는 항목들을 확인할 수 있습니다.
- 각 테이블의 튜플 수
- 각 테이블의 데드 튜플 수
- 각 테이블의 마지막 Vacuum 수행 시간
- 각 테이블의 마지막 Analyze 수행 시간
- 각 테이블의 튜플 Insert / Update / Delete 비율
- 각 테이블의 Autovacuum에 소요되는 시간
- Manual VACUUM / ANALYZE 후 동일한 쿼리 수행
감사합니다.