dev.to

PostgreSQL 19 beta's parallel autovacuum launches workers but leaves vacuum time unchanged

PostgreSQL 19 베타의 병렬 autovacuum, 작업자를 늘려도 vacuum 시간은 그대로

PostgreSQL 19 beta4는 autovacuum의 인덱스 정리에 병렬 작업자를 투입하는 설정을 추가합니다. 한 실험에서 autovacuum 작업자의 CPU 시간은 약 30% 줄었지만 전체 vacuum 시간은 약 4%만 짧아졌습니다. 이 결과는 heap 스캔이 병목인 작업에서는 병렬 인덱스 정리만으로 처리 시간이 줄지 않음을 보여줍니다.

에디터 노트

이 실험에서 분명하게 확인된 효과는 전체 vacuum 시간이 아니라 autovacuum 작업자의 CPU 사용량 감소입니다. wall-clock 차이는 약 4%로 실행 간 변동 범위 안이었고, 저자도 heap 스캔과 WAL이 병목이었다고 기록했습니다. 따라서 결과는 병렬 autovacuum의 일반적인 처리 시간 개선을 입증하기보다 병렬화한 단계가 병목이 아닐 때의 한계를 보여줍니다. 댓글은 이 조건을 더 구체화합니다. 저자는 scoring이 작업자 실행 여부를 정하지 않으며, 테이블 선택 순서와 작업자 배정은 별개라고 설명합니다. 인덱스 정리가 병목인 더 큰 인덱스 구성의 실험은 아직 제시되지 않았으므로, 작업자 수를 늘렸을 때 전체 시간이 줄어드는 조건은 이 자료만으로 판단하기 어렵습니다. 반면 전역 작업자 풀 제한과 열린 snapshot이 vacuum을 막은 사례는 설정과 운영 상태가 성능에 미치는 범위를 직접 보여줍니다.

AI 요약

PostgreSQL 19 beta4는 백그라운드 autovacuum이 여러 작업자로 테이블 인덱스를 정리하도록 하는 설정 autovacuum_max_parallel_workers를 추가합니다. 기본값은 0이라 직접 켜야 합니다. 글쓴이는 인덱스 6개가 있는 40만 행 테이블을 만들고 각 행의 인덱스 대상 컬럼을 두 번 갱신해 dead tuple 80만 개를 남긴 뒤, 매번 테이블을 새로 만들어 세 차례씩 측정했습니다.

병렬화로 줄어든 것은 작업자 CPU 시간입니다

병렬 작업자를 쓰지 않은 세 번의 wall-clock 시간은 1.24초, 1.36초, 1.32초로 평균 1.31초였습니다. autovacuum_max_parallel_workers를 4로 설정한 경우에는 1.23초, 1.38초, 1.14초로 평균 1.25초였습니다. 약 4% 차이지만 글쓴이는 실행 간 변동 범위 안이라고 봅니다. 반면 autovacuum 작업자 프로세스의 CPU 시간은 평균 약 0.56초에서 약 0.40초로 30%가량 줄었습니다. 로그에는 인덱스 정리에 작업자 4명이 계획되고 4명이 실행됐다고 기록됐습니다.

이 실험에서 전체 시간의 병목은 병렬화되지 않은 heap 스캔과 그 과정에서 발생한 WAL이었습니다. 실행마다 WAL 레코드가 10만 건 이상, dirty buffer가 7만 개 이상 기록됐습니다. 인덱스 정리가 전체 작업 시간에서 차지하는 비중이 크지 않아 병렬화 효과가 wall-clock 시간으로 이어지지 않았습니다. 글쓴이는 인덱스 정리가 병목인 테이블에서는 다른 결과가 나올 수 있다고 덧붙입니다.

메모리와 작업자 수를 제한하는 설정

로그에 기록된 dead-tuple TID 저장 공간은 작업자를 쓰지 않았을 때 1.45MB였습니다. 작업자 4명을 쓴 경우에는 3.00MB로 늘었습니다. 같은 dead tuple을 처리해도 작업자마다 bookkeeping 공간을 나눠 쓰기 때문입니다. 작은 실험에서는 부담이 작지만 autovacuum_work_mem이 빠듯한 대형 테이블에서 여러 autovacuum이 병렬 작업자를 함께 쓰면 이 증가분을 고려해야 한다고 설명합니다.

작업자 수를 제한하는 설정도 구분해야 합니다. PostgreSQL 11부터 있는 max_parallel_maintenance_workers는 수동 VACUUM (PARALLEL)이나 CREATE INDEX의 작업자 수를 제어하지만, 새 autovacuum 병렬 작업자에는 적용되지 않았습니다. 글쓴이가 이 값을 0으로 두고 autovacuum_max_parallel_workers를 4로 설정해도 작업자 4명이 실행됐습니다. 실제 작업자 수는 전역 설정 max_parallel_workers의 제한을 받았습니다. 이 값을 0으로 두자 4명을 계획했지만 실행한 작업자는 0명이었습니다.

우선 처리 순서와 장기 트랜잭션

dead tuple이 각각 100, 500, 5,000, 40,000, 150,000, 500,000개인 테이블 6개를 만든 순서와 반대로, autovacuum은 dead tuple이 가장 많은 테이블부터 처리했습니다. 이 실험에서는 새 scoring system이 dead tuple 수가 많은 테이블을 먼저 선택하는 순서를 보였습니다.

전역 max_parallel_workers를 4로 제한하고 그중 3개를 장시간 실행하는 병렬 쿼리에 할당한 뒤 autovacuum을 실행하자, 약 40초 동안 반복해서 작업자 4명을 계획하고도 한 명도 실행하지 못했습니다. 동시에 열린 쿼리의 snapshot이 dead tuple 80만 개를 아직 제거할 수 없게 막아 vacuum은 아무것도 지우지 못했습니다. 쿼리 세션을 종료하자 다음 주기에 작업자 4명이 실행됐고 약 1초 만에 dead tuple을 정리했습니다. 병렬 작업자 수를 늘려도 오래 열린 트랜잭션이 vacuum을 막는 문제는 해결되지 않습니다.

진행 상황과 실험 설정의 주의점

PostgreSQL 19의 pg_stat_progress_vacuum은 heap 스캔과 인덱스 정리 단계를 보여주며 indexes_total과 indexes_processed로 인덱스 처리 진행도도 확인합니다. 글쓴이는 이를 이용해 느린 vacuum이 heap을 읽는 중인지 인덱스를 정리하는 중인지 구분했습니다. 설정 실험에서는 서버 실행 명령줄에 지정한 -c 값이 ALTER SYSTEM 설정을 덮어쓰는 점도 확인했습니다. pg_reload_conf()가 적용됐다고 보고해도 명령줄 설정은 서버를 다시 시작하기 전까지 유지됩니다.

dev.to 반응

  • @mrsaynothing — wall-clock 시간이 증거입니다. serial 1.31초와 parallel 1.25초의 차이는 실행 간 변동 범위 안입니다. 이 기능은 비용을 옮겼지만 걸리는 시간은 줄이지 않았습니다. 두 표를 나란히 보여준 것이 정직한 방식입니다. CPU 감소는 실제로 있었지만 시간은 그대로였다는 점이 남길 만한 내용입니다. 한 가지 보완하자면, 작은 인덱스 6개에 dead tuple 80만 개가 있는 상황은 작업자 비용을 상쇄할 만큼 일이 많지 않을 수 있습니다. 인덱스 vacuum이 전체 시간을 지배하는 테이블에서 작업자 4명이 실제로 시간을 줄이는지, 아니면 scoring 때문에 작업자가 실행되지 않는지 확인해야 합니다.
    • @alexgeorgiev17 — 감사합니다. "비용을 옮겼지만 걸리는 시간은 줄이지 않았다"가 제가 쓴 표현보다 낫네요. 보완 의견에도 동의합니다. 이 테이블은 heap 스캔과 그때 기록한 WAL이 느린 부분이라 인덱스 단계를 병렬화해도 줄일 시간이 많지 않았습니다. 인덱스 정리가 대부분의 시간을 차지하는 테이블에서 시험해야 합니다. 다만 scoring system은 작업자 실행 여부를 결정하지 않습니다. scoring은 autovacuum이 어떤 테이블을 먼저 처리할지 정합니다. 작업자 수는 테이블별 인덱스에 따라 정해지며, 제가 알기로 min_parallel_index_scan_size보다 작은 인덱스에는 작업자가 배정되지 않습니다. 이 실험에서는 매번 작업자 4명을 계획하고 4명을 실행했으므로 실행 여부가 문제가 아니라 작업자에게 배정된 일이 병목이 아니었던 것입니다. 다시 시험한다면 heap은 작게 하고 인덱스는 크게 만들겠습니다. 행을 적게 두고 넓게 만들며, 자주 바뀌는 컬럼에 인덱스를 10여 개 두어 인덱스 단계가 병목이 되게 하겠습니다. 그런 조건에서도 작업자 4명이 wall-clock 시간을 줄이지 못한다면 더 강한 결과가 될 것입니다.

원문: dev.to / 번역·요약: Trawling