PostgreSQL autovacuum 튜닝
GitLab v19.5Offering: GitLab Self-Managed
요약
이 페이지를 사용해 PostgreSQL autovacuum을 튜닝하고 일반적인 autovacuum 문제를 해결합니다. PostgreSQL은 다중 버전 동시성 제어(MVCC)를 사용합니다. autovacuum은 데이터베이스 상태에 매우 중요합니다.
이 페이지를 사용해 PostgreSQL autovacuum을 튜닝하고 일반적인 autovacuum 문제를 해결합니다.
autovacuum 작동 방식#
PostgreSQL은 다중 버전 동시성 제어(MVCC)를 사용합니다. 행을 업데이트하거나 삭제해도 PostgreSQL은 이전 행 버전을 즉시 제거하지 않습니다. 대신 이전 버전을 dead로 표시하고, 실행 중인 어떤 트랜잭션도 그 버전을 볼 수 없게 될 때까지 보관합니다. autovacuum은 이렇게 dead 상태가 된 행 버전(dead tuple이라고도 함)을 제거하는 백그라운드 프로세스입니다. dead tuple이 차지한 공간을 회수하고, 플래너 통계를 업데이트하며, 가시성 맵(visibility map)을 유지합니다.
autovacuum은 데이터베이스 상태에 매우 중요합니다. autovacuum이 없으면 dead 행이 테이블과 인덱스 블로트(bloat)로 쌓입니다. 블로트는 쿼리를 느리게 만들고, 디스크 공간을 낭비하며, 백업 및 유지 관리 시간을 늘립니다. autovacuum은 또한 트랜잭션 ID 랩어라운드를 방지합니다. 랩어라운드가 발생하면 데이터베이스가 쓰기를 받지 않게 됩니다.
autovacuum 실행 시점#
autovacuum은 각 테이블을 주기적으로 확인하고, dead tuple 수가 다음 임계값을 넘으면 vacuum을 실행합니다.
autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor × number_of_live_tuples
기본적으로 PostgreSQL은 autovacuum_vacuum_scale_factor를 0.2로 설정합니다. 그러면 PostgreSQL은 테이블 행의
20%가 dead 상태가 된 뒤에야 vacuum을 실행합니다. 큰 테이블에서 20%는 매우 많은 dead tuple입니다. 그만큼
테이블이 오랫동안 블로트된 뒤에야 autovacuum이 동작합니다. 이 지연을 줄이기 위해 GitLab
Linux 패키지 설치는 autovacuum_vacuum_scale_factor를 기본값 0.02(2%)로 낮춥니다. 가장
크거나 가장 자주 업데이트되는 테이블에는 여전히 테이블별로 더 낮은 값이 필요할 수 있습니다.
큰 테이블을 개별적으로 튜닝을 참고합니다.
권장 autovacuum 설정#
GitLab Linux 패키지 설치는 대부분의 워크로드에 맞는 autovacuum 기본값을 제공합니다. 모니터링에서 autovacuum이 dead tuple을 따라가지 못하는 것으로 확인되면 이 설정을 변경합니다. 목표는 I/O를 고갈시키지 않으면서, 워크로드가 dead tuple을 만들어 내는 속도만큼 빠르게 autovacuum이 dead tuple을 회수하도록 하는 것입니다.
가장 큰 효과를 내는 변경은 보통 autovacuum_vacuum_cost_limit 상향, autovacuum_max_workers 상향,
그리고 가장 큰 테이블의 scale factor 하향입니다. 이 절의 값은 시작점으로 보고, 사용자의 워크로드에서
검증합니다.
클러스터 전체 설정#
GitLab Linux 패키지 설치에서는 /etc/gitlab/gitlab.rb에 클러스터 전체 값을 설정한 다음
sudo gitlab-ctl reconfigure를 실행합니다.
postgresql['autovacuum_max_workers'] = "<value>"
postgresql['autovacuum_vacuum_cost_limit'] = "<value>"
postgresql['autovacuum_vacuum_cost_delay'] = "<value>"
postgresql['maintenance_work_mem'] = "<value>"
외부 PostgreSQL 인스턴스에서는 대신 postgresql.conf에 같은 매개변수를 설정한 다음
PostgreSQL을 다시 로드합니다.
autovacuum_max_workers = <value>
autovacuum_vacuum_cost_limit = <value>
autovacuum_vacuum_cost_delay = <value>
maintenance_work_mem = <value>
autovacuum_work_mem = <value>
SELECT pg_reload_conf();를 실행하거나 서비스를 다시 로드해 PostgreSQL을 다시 로드합니다. 두 설치 유형 모두에서
autovacuum_max_workers 같은 설정은 PostgreSQL을 완전히 재시작해야 적용됩니다.
다시 로드하거나 reconfigure하는 것만으로는 충분하지 않습니다.
다음 설정은 autovacuum이 작업을 따라가는지 여부에 가장 큰 영향을 줍니다.
| 설정 | 권장 값 | 설명 |
|---|---|---|
autovacuum_max_workers |
vCPU 수의 약 25% | 동시에 실행되는 autovacuum 프로세스의 최대 수입니다. 멀티 코어를 갖춘 대형 데이터베이스 호스트에는 기본값이 너무 낮은 경우가 많습니다. 워커가 늘어날 때마다 메모리와 I/O 부하가 추가됩니다. |
autovacuum_vacuum_scale_factor |
0.01에서 0.02 |
autovacuum이 실행되기 전에 dead 상태여야 하는 테이블 행의 비율입니다. GitLab은 PostgreSQL 기본값을 0.02로 낮춥니다. 자주 업데이트되는 큰 테이블에는 이를 더 낮춥니다. 큰 테이블을 개별적으로 튜닝을 참고합니다. |
autovacuum_vacuum_cost_limit |
SSD의 경우 800 | autovacuum이 일시 중지하기 전까지 누적하는 I/O 비용입니다. -1로 설정하면 vacuum_cost_limit 값을 상속합니다. 이 값은 최신 SSD 기반 스토리지의 처리량보다 훨씬 낮습니다. 명시적으로 설정합니다. 이 값을 올리는 것이 가장 효과가 큰 변경인 경우가 많습니다. |
autovacuum_vacuum_cost_delay |
2 ms | autovacuum이 비용 한도에 도달한 뒤 일시 중지하는 시간입니다. 값을 낮추면 autovacuum이 더 공격적으로 동작합니다. 0으로 설정하지 않습니다. 피해야 할 설정을 참고합니다. |
maintenance_work_mem |
1 GB에서 2 GB | 인덱스 생성 같은 유지 관리 작업에 사용하는 메모리입니다. autovacuum_work_mem이 -1이면 각 autovacuum 워커도 dead tuple을 추적하는 데 이 크기만큼 사용합니다. 값이 낮으면 큰 테이블에서 워커가 인덱스를 반복해서 스캔합니다. 사용 가능한 RAM과 워커 수를 고려해 크기를 정합니다. |
autovacuum_work_mem |
512 MB | 각 autovacuum 워커가 dead tuple을 추적하는 데 사용하는 메모리입니다. -1로 설정하면 워커는 maintenance_work_mem을 상속합니다. 그러면 전체 autovacuum 메모리가 워커 수에 따라 늘어납니다. PostgreSQL 17에서는 이 메모리의 1 GB 상한이 제거되었습니다. 워커 수를 늘릴 때는 명시적으로 한도를 정합니다. Linux 패키지 설치에서는 gitlab.rb에서 이 매개변수를 설정할 수 없습니다. |
autovacuum_naptime |
몇 초(선택 사항) | autovacuum이 데이터베이스를 확인하는 간격 사이에 기다리는 시간입니다. 변동이 빠른 데이터베이스에서는 값을 낮춰 autovacuum이 더 빠르게 반응하도록 합니다. |
큰 테이블을 개별적으로 튜닝#
가장 큰 테이블은 클러스터 전체 scale factor가 낮아도 블로트될 수 있습니다. 이러한 테이블에는 PostgreSQL에서 직접
테이블별로 더 낮은 scale factor 또는 임계값을 설정합니다. 자주 업데이트되는 매우 큰 테이블에는
0.01(1%) 정도의 scale factor가 일반적입니다.
ALTER TABLE <table_name> SET (autovacuum_vacuum_scale_factor = 0.01);
테이블별 설정은 해당 테이블에 한해 클러스터 전체 값을 덮어씁니다. 모니터링을 사용해 뒤처지는 테이블을 찾고, 변경 사항은 먼저 비프로덕션 환경에서 테스트합니다.
피해야 할 설정#
autovacuum을 비활성화(autovacuum = off)하지 않습니다. autovacuum이 없으면 dead tuple이 쌓입니다.
그러면 데이터베이스는 트랜잭션 ID 랩어라운드로 향합니다. 랩어라운드가 발생하면 데이터베이스가 쓰기를
받지 않게 됩니다.
다음 구성은 피합니다.
autovacuum_vacuum_cost_delay = 0은 I/O 스로틀링을 없앱니다. 그러면 autovacuum이 멈춤 없이 실행되어 디스크 I/O를 포화시킬 수 있습니다. 쿼리가 느려집니다.- 가장 큰 테이블에 기본 scale factor를 사용하는 것. 그러면 이러한 테이블은 vacuum이 너무 드물게 실행됩니다. 대신 테이블별로 더 낮은 값을 설정합니다.
autovacuum_vacuum_cost_limit을-1상속에 의존하는 것. 그러면 실제 값이 다른 설정에 있어 놓치기 쉽습니다. 명시적으로 설정합니다.
autovacuum 모니터링#
데이터베이스 진단 페이지#
히스토리
Database diagnostics 페이지는 각 데이터베이스의 vacuum 활동과 autovacuum 설정을 보여 줍니다.
사전 요구 사항:
- 관리자 액세스 권한이 있어야 합니다.
페이지를 보려면 다음을 수행합니다.
- 오른쪽 위 모서리에서 Admin을 선택합니다.
- 왼쪽 사이드바에서 Monitoring > Database diagnostics를 선택합니다.
페이지의 두 섹션이 autovacuum과 관련됩니다. 각 섹션에는 설치에 포함된 데이터베이스마다 카드가 하나씩 있습니다.
Vacuum information 섹션에는 Vacuum activity 패널이 있습니다. 이 패널에는 지금 실행 중인 vacuum마다 행이 하나씩 있습니다. 칼럼은 Table, Type, Running for, Phase, Heap scanned, Indexes processed, Dead tuples, Index passes, Delay time입니다. 행에는 다음 배지가 표시될 수 있습니다.
- Anti-wraparound: vacuum이 트랜잭션 ID 랩어라운드를 방지하고 있습니다. 이 vacuum은 자동으로 취소되지 않습니다. 종료하지 않습니다. 종료하면 데이터를 보호하기 위해 데이터베이스가 종료될 수 있습니다.
- Long-running: vacuum이 6시간 넘게 실행되었습니다. 큰 테이블은 이 정도 걸릴 수 있지만, 끝나지 않는 vacuum은 차단되었거나 리소스를 받지 못하는 상태일 수 있습니다.
- Memory pressure: vacuum에 인덱스 패스가 두 번 이상 필요했습니다. 힙 스캔이 끝나기 전에 dead tuple 저장 공간이
가득 찼습니다.
maintenance_work_mem또는autovacuum_work_mem을 올립니다.
Autovacuum configuration 섹션에는 다음 패널이 있습니다.
- Effective settings: 데이터베이스가 실제로 사용하는 autovacuum 설정으로,
pg_settings에서 읽습니다. Status 칼럼은 검사에서 발견된 잘못된 구성을 각각 표시합니다.autovacuum_vacuum_cost_limit이-1이면 이 패널은vacuum_cost_limit에서 상속하는 실제 한도도 함께 표시합니다. - Per-table overrides: 스토리지 매개변수에 autovacuum 매개변수를 설정한 테이블입니다. 이 패널은 각 테이블의 크기, 추정 행 수, 재정의 내용을 표시합니다. autovacuum을 비활성화한 테이블에는 Autovacuum disabled 배지가 표시됩니다. 이 패널은 최대 50개의 테이블을 나열하며, autovacuum을 비활성화한 테이블이 먼저 나옵니다. 재정의가 있는 테이블이 없으면 이 패널은 숨겨집니다.
- Scale factor risk: 적용 중인 scale factor가 여전히 높은 큰 테이블입니다. 검사에서 해당하는 테이블이 없으면 이 패널은 숨겨집니다.
일부 발견 사항은 개별 설정이 아니라 테이블에 관한 것입니다. 이러한 발견 사항은 패널 위에 경고로 표시됩니다. 각 발견 사항의 조건은 Autovacuum 발견 사항을 참고합니다.
Vacuum activity 패널은 PostgreSQL 17 이상이 필요합니다. 이전 버전에서는 이 패널이
비어 있습니다. Delay time 칼럼은 PostgreSQL 18 이상이 필요합니다. Type, Running for 칼럼과
Anti-wraparound 배지는 다른 백엔드의 활동을 읽을 수 있는 데이터베이스 역할이 필요합니다.
해당 역할을 pg_monitor의 멤버로 추가합니다. 이 멤버십이 없으면 진행 상황 칼럼은 계속 작동하지만,
Type, Running for, Anti-wraparound 필드에는 Not available이 표시됩니다.
두 섹션 모두 기본 데이터베이스에서 읽습니다. autovacuum이 그곳에서만 실행되기 때문입니다.
콘솔에서 진단 보고#
히스토리
UI에 접근할 수 없거나 스크립트에서 보고서를 가져오고 싶을 때 Rake 태스크를 사용합니다.
Linux 패키지 설치의 경우:
sudo gitlab-rake gitlab:db:diagnostics
기본적으로 이 태스크는 모든 데이터베이스를 보고합니다. 데이터베이스를 선택하려면 이름을 전달합니다.
sudo gitlab-rake "gitlab:db:diagnostics[main,ci]"
autovacuum 검사만 실행하려면 전용 태스크를 사용합니다. 같은 데이터베이스 이름을 받습니다.
sudo gitlab-rake "gitlab:db:diagnostics:autovacuum_settings[main,ci]"
보고서는 요약을 먼저 출력한 다음 검사마다 섹션을 출력합니다. Autovacuum settings 섹션에는 각 데이터베이스의 발견 사항, 유효 설정, 테이블별 재정의, scale factor 위험이 나열됩니다.
이 태스크는 오류를 발견하면 종료 상태 1로 끝나므로, 모니터링 작업에서 종료 코드를 사용할 수 있습니다.
터미널에서는 이 태스크가 검사를 둘 이상 실행할 때 각 섹션을 여는 메뉴를 표시합니다. 검사 하나를 실행하거나 출력을 파이프 또는 리디렉션하면 태스크가 전체 보고서를 출력합니다.
Rake 태스크는 설정만 보고합니다. vacuum 활동은 표시하지 않습니다. vacuum 활동은 Database diagnostics 페이지에서만 확인할 수 있습니다.
Autovacuum 발견 사항#
Database diagnostics 페이지와 Rake 태스크는 같은 검사를 사용합니다. 둘 다 같은 발견 사항을 보고합니다. 처음 다섯 가지 발견 사항은 각각 하나의 설정에 관한 것이며 Effective settings 패널의 Status 칼럼에 표시됩니다. 마지막 두 가지는 테이블에 관한 것이며 경고로 표시됩니다.
| 발견 사항 | 설정 | 심각도 | 보고 조건 | 조치 |
|---|---|---|---|---|
| Disabled | autovacuum |
Error | autovacuum이 off입니다. dead tuple이 자동으로 회수되지 않습니다. 블로트가 발생할 위험이 있으며 결국 트랜잭션 ID 랩어라운드로 이어질 수 있습니다. |
autovacuum을 켭니다. 피해야 할 설정을 참고합니다. |
| Throttling disabled | autovacuum_vacuum_cost_delay |
Error | 비용 지연이 0입니다. 그러면 autovacuum이 전속력으로 실행됩니다. 쓰기 폭주와 복제 지연이 발생할 수 있습니다. |
0이 아닌 지연을 설정합니다. 피해야 할 설정을 참고합니다. |
| Low | autovacuum_max_workers |
Warning | 워커가 세 개 미만으로 구성되어 있습니다. 큰 데이터베이스나 분리된(decomposed) 데이터베이스에는 너무 적을 수 있습니다. | 워커 수를 늘립니다. 클러스터 전체 설정을 참고합니다. |
| Low | autovacuum_vacuum_cost_limit |
Warning | 유효 한도가 200 이하입니다. 이 한도는 최신 스토리지에는 너무 낮을 가능성이 높습니다. 설정이 -1이면 검사는 vacuum_cost_limit에서 상속한 값을 사용합니다. Linux 패키지 설치는 기본적으로 -1을 제공합니다. PostgreSQL의 vacuum_cost_limit 기본값은 200입니다. 따라서 이 발견 사항은 기본 Linux 패키지 설치에서 나타납니다. |
한도를 명시적으로 설정합니다. 클러스터 전체 설정을 참고합니다. |
| Inherited | autovacuum_work_mem |
Warning | 설정이 -1입니다. 각 워커가 maintenance_work_mem을 상속합니다. |
명시적으로 설정해 각 워커가 사용하는 메모리를 제한합니다. Linux 패키지 설치에서는 gitlab.rb에서 이 매개변수를 설정할 수 없으므로 대신 maintenance_work_mem을 튜닝합니다. 클러스터 전체 설정을 참고합니다. |
| Tables disabled | 테이블별 autovacuum_enabled |
Error | 하나 이상의 테이블이 스토리지 매개변수에 autovacuum_enabled = false를 설정했습니다. 해당 테이블의 dead tuple은 자동으로 회수되지 않습니다. Per-table overrides 패널이 해당하는 테이블을 각각 표시합니다. |
ALTER TABLE <table_name> RESET (autovacuum_enabled);로 각 테이블의 autovacuum을 다시 켭니다. |
| Scale factor risk | autovacuum_vacuum_scale_factor |
Warning | 클러스터 전체 scale factor가 0.1 이상이고, 가장 큰 테이블 20개 중 하나가 10 GiB 이상이면서 적용 중인 scale factor가 0.1 이상입니다. Linux 패키지 설치는 0.02를 제공합니다. 따라서 이 발견 사항은 주로 PostgreSQL 기본값 0.2를 유지하는 외부 PostgreSQL 인스턴스에서 나타납니다. |
클러스터 전체 scale factor를 낮추거나 나열된 테이블에 테이블별 값을 설정합니다. 큰 테이블을 개별적으로 튜닝을 참고합니다. |
적용 중인 scale factor는 테이블별 재정의가 설정되어 있으면 그 값이고, 그렇지 않으면 클러스터 전체 값입니다.
테이블별 autovacuum_vacuum_threshold가 있다고 해서 그 테이블이 검사에서 제외되지는 않습니다. 검사는
autovacuum이 비활성화된 테이블을 건너뜁니다.
검사는 명백한 잘못된 구성만 보고합니다. 또한 검사는 권장 autovacuum 설정의 값보다 낮은 임계값을 사용합니다. 설정이 검사를 통과해도 워크로드에는 여전히 너무 낮을 수 있습니다. 예를 들어 autovacuum 워커 세 개는 검사를 통과하지만, vCPU가 많은 호스트에는 적은 수입니다.
dead tuple과 freeze age 확인#
Database diagnostics 페이지와 Rake 태스크는 모두 테이블별 dead tuple이나 freeze age를 보고하지 않습니다. 해당 정보는 데이터베이스를 직접 쿼리해 확인합니다. Linux 패키지 설치에서는 다음으로 연결합니다.
sudo gitlab-psql
dead tuple이 가장 많은 테이블과 autovacuum이 마지막으로 처리한 시점을 찾으려면 다음을 실행합니다.
SELECT schemaname, relname, n_live_tup, n_dead_tup, last_autovacuum, last_autoanalyze
FROM pg_stat_user_tables
WHERE n_dead_tup > 0
ORDER BY n_dead_tup DESC
LIMIT 20;
n_dead_tup 수가 증가하는데 last_autovacuum이 오래되었거나 비어 있다면, autovacuum이 해당 테이블을
따라가지 못한다는 뜻입니다.
autovacuum이 테이블을 따라가지 못함을 참고합니다.
각 테이블의 freeze age를 찾으려면 다음을 실행합니다.
SELECT n.nspname AS schema_name, c.relname AS table_name, age(c.relfrozenxid) AS xid_age
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'm')
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY xid_age DESC
LIMIT 20;
xid_age는 PostgreSQL이 테이블을 마지막으로 동결(freeze)한 이후 실행된 트랜잭션 수입니다. 이 값을
autovacuum_freeze_max_age와 비교합니다. Effective settings 패널에서 이 값을 확인할 수 있습니다.
테이블이 이 값을 넘으면 PostgreSQL이 해당 테이블에 대해 anti-wraparound autovacuum을 시작합니다. freeze age가
계속 올라가면 autovacuum이 행을 충분히 빠르게 동결하지 못한다는 뜻입니다.
트랜잭션 ID 랩어라운드 임박을 참고합니다.
일반적인 문제 해결#
autovacuum이 테이블을 따라가지 못함#
테이블에서 dead tuple이 autovacuum이 제거하는 속도보다 빠르게 쌓입니다. dead tuple 수가 계속 증가하고, 테이블이 블로트되며, 해당 테이블에 대한 쿼리가 느려집니다. 이 문제의 일반적인 원인은 두 가지입니다.
- 높은 변경 비율. 데이터베이스 마이그레이션이나 행을 자주 업데이트하는 엔드포인트는 autovacuum이 정리하는 속도보다 빠르게 dead tuple을 만듭니다.
- 클러스터 전체 설정을 사용하는 바쁜 테이블. 자주 업데이트되는 큰 테이블에는 대개 자체 autovacuum 설정이 필요합니다.
뒤처지는 테이블을 찾으려면 각 테이블의 dead tuple 수와 autovacuum이 마지막으로 실행된 시점을 확인합니다. autovacuum 모니터링을 참고합니다. 문제를 해결하려면 다음을 수행합니다.
- 테이블별 scale factor 또는 임계값을 더 낮게 설정합니다. 자세한 내용은 큰 테이블을 개별적으로 튜닝을 참고합니다.
autovacuum_vacuum_cost_limit과autovacuum_max_workers를 올립니다. 자세한 내용은 권장 autovacuum 설정을 참고합니다.- 마이그레이션이나 애플리케이션 변경이 변경 비율의 원인이면 일시 중지하거나 되돌립니다.
트랜잭션 ID 랩어라운드 임박#
PostgreSQL은 각 트랜잭션에 ID(XID)를 할당합니다. 동결되지 않은 가장 오래된 XID가 오래될수록, PostgreSQL은 anti-wraparound autovacuum을 실행해 오래된 행을 동결합니다. 이러한 vacuum이 완료되지 못하면 데이터를 보호하기 위해 데이터베이스가 결국 쓰기를 받지 않게 됩니다.
이 위험을 추적하려면 각 데이터베이스와 테이블의 freeze age를 모니터링합니다. autovacuum 모니터링을 참고합니다. freeze age가 올라간다는 것은 autovacuum이 행을 충분히 빠르게 동결하지 못한다는 뜻입니다.
이를 해결하려면 autovacuum이 실행되고 차단되지 않았는지 확인합니다. autovacuum이 테이블을 따라가지 못함을 참고합니다. 데이터베이스가 이미 쓰기를 받지 않는 상태라면 데이터베이스가 랩어라운드 데이터 손실을 피하기 위해 명령을 받지 않음의 복구 단계를 따릅니다.
autovacuum이 리소스를 너무 많이 사용함#
autovacuum은 다른 쿼리를 느리게 만들 수 있는 I/O 트래픽을 만듭니다. PostgreSQL은 비용 기반 지연으로 이 트래픽을 제한합니다. 이 지연은 실행 중인 모든 autovacuum 워커에 걸쳐 균형을 이룹니다. 따라서 워커가 몇 개 실행되든 전체 I/O 영향은 거의 같게 유지됩니다.
다른 쿼리에 미치는 영향을 줄이려면 autovacuum_vacuum_cost_delay를 올려 워커가 더 자주 일시 중지하도록 합니다.
지연이 길수록 vacuum이 느려집니다. dead tuple을 따라가야 할 필요와 균형을 맞춥니다. 지연을 0으로 설정하지 않습니다.
그러면 스로틀링이 완전히 사라집니다. 자세한 내용은
피해야 할 설정을 참고합니다.