TechFeedTechFeed
Backend

오토배큠, 죽은 튜플, 트랜잭션 ID, 랩어라운드 | 디스크는 느는데 쿼리가 왜 느려지나?

포스트그레 오토배큠이 막히면 알디에스 디스크만 늘고 쿼리가 느려진다. 죽은 튜플, 일반 배큠과 풀 배큠, 트랜잭션 ID 랩어라운드, 복제 슬롯, 적응형 오토배큠, MaximumUsedTransactionIDs 알람을 백엔드, PostgreSQL, API, 클라우드, 개발자, 데이터 기준으로 같은 표에 둔다. 피지바운서와 쿼리 성능 글과 각을 섞지 않는다.

by

알디에스 디스크가 느는데 행 수는 그대로면, 인스턴스를 키우기 전에 죽은 튜플과 오토배큠이 막힌 자리를 봅니다. 업데이트와 삭제는 옛 행 버전을 바로 지우지 않습니다. 일반 배큠이 그 자리를 재사용 표시로 돌리고, 운영체제 파일을 줄이는 일은 풀 배큠뿐입니다. 풀은 테이블을 통째로 잠급니다. 트랜잭션 아이디는 32비트라 약 20억 번 안에 모든 테이블을 한 번은 얼려야 합니다. 공식 기본 동결 나이는 2억입니다. 저는 국내 알디에스에서 디스크 알람보다 트랜잭션 아이디 사용량과 오래된 복제 슬롯을 먼저 봅니다.


풀 고갈은 피지바운서 케이스를, 실행 계획은 쿼리 성능 글을 보면 자리가 겹치지 않습니다. 숫자는 2026년 8월 포스트그레 18 문서와 알디에스 가이드 기준입니다.


디스크가 느는데 행 수는 그대로인 이유는?

업데이트와 삭제는 옛 행을 바로 지우는 명령이 아닙니다. 다른 트랜잭션이 아직 그 버전을 볼 수 있어서, 자리는 죽은 튜플로 남습니다. 일반 배큠이 그 페이지를 재사용 가능하게 표시합니다. 파일 끝을 비우지 못하면 운영체제 용량은 그대로입니다.


제가 본 국내 알디에스 밤은 흔합니다. 주문 테이블에 상태 컬럼만 밤새 갈아엎었는데, 행 수는 어제와 같고 pg_total_relation_size만 커집니다. 콘솔 디스크 알람이 뜨면 인스턴스 클래스를 한 단계 올립니다. 다음 주도 같은 기울기입니다. 쿼리가 느린 자리는 실행 계획보다 힙이 부푼 자리인 경우가 많습니다. 그 칸은 실행 계획 글과 겹치지 않습니다. 여기는 공간이 안 돌아오는 이유입니다.


풀 배큠은 새 파일을 통째로 씁니다. 테이블을 액세스 익스클루시브로 잠그고, 복사본만큼 디스크가 잠깐 더 필요합니다. 오토배큠은 풀 배큠을 치지 않습니다. 공식 문서도 일상 유지는 일반 배큠을 자주 돌리는 쪽이라고 적습니다. 피크에 풀을 치면 락만 깁니다. 스키마를 나누는 밤은 무중단 마이그레이션을 같이 봅니다.


한 줄 가드 | 디스크가 늘었다고 풀 배큠부터 치지 않습니다. 먼저 죽은 튜플 비율과 마지막 오토배큠 시각을 같은 표에 올립니다.


죽은 튜플이 20퍼센트를 넘기면 어디가 먼저 느려지나

기본 스케일 팩터는 0.2입니다. 산 행이 100만이면 죽은 행이 약 20만 플러스 임계 50을 넘길 때 오토배큠이 후보에 오릅니다. 그 전에 시퀀셜 스캔이 부푼 힙을 읽고, 인덱스만 보는 스캔은 가시성 맵이 안 따라오면 힙을 다시 칩니다.


큰 테이블은 20퍼센트가 너무 늦습니다. 1억 행이면 2천만 죽은 행을 기다립니다. 그때는 테이블 단위로 스케일을 0.05나 0.02로 내립니다. 작은 설정 테이블은 기본값으로도 충분합니다. 워커를 한꺼번에 올리면 아이오가 몰립니다. 큰 테이블은 워커를 적게, 메모리를 많이 줍니다.


항목일반 배큠풀 배큠
셰어 업데이트 익스클루시브. 조회와 쓰기는 같이 감액세스 익스클루시브. 테이블을 통째로 잠금
디스크자리를 재사용. 파일은 거의 안 줄어듦새 복사본을 쓰고 파일을 줄임. 잠깐 용량이 두 배
누가 치나오토배큠 기본 경로오토배큠은 치지 않음. 사람이 고름
통계애널라이즈가 따로 또는 같이 돎테이블을 다시 쓰므로 통계도 같이 갱신
맞는 자리매일 업데이트되는 주문, 세션, 이벤트대량 삭제 뒤 파일이 비고, 한동안 안 커질 때

알디에스는 14 이하에서 오토배큠 워크 메모리가 -1이면 메인터넌스 워크 메모리를 씁니다. 16까지는 배큠 메모리가 1기가에 막혀, 죽은 튜플이 약 1억 7900만을 넘으면 인덱스를 여러 번 돕니다. 17부터는 그 상한이 없습니다. 버전을 올리기 전에 죽은 튜플 수에 6바이트를 곱해 한 번에 필요한 메모리를 가늠합니다.


죽은 튜플과 마지막 오토배큠을 같은 표에 올린다
SELECT relname, n_live_tup, n_dead_tup, round(n_dead_tup * 100.0 / nullif(n_live_tup + n_dead_tup, 0), 1) AS dead_pct, last_autovacuum, last_autoanalyze FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20;
데이터베이스 서버 랙과 모니터링이 보이는 백엔드 운영 장면
디스크만 보고 인스턴스를 키우면 죽은 튜플은 그대로다

오토배큠을 꺼 두면 안전하지 않다

꺼도 동결 나이가 한계를 넘으면 랩어라운드 방지 배큠이 강제로 뜹니다. 트랜잭션 아이디는 32비트입니다. 약 20억 번을 넘기기 전에 모든 테이블을 한 번은 얼려야 과거 행이 미래처럼 보이지 않습니다. 기본 autovacuum_freeze_max_age는 2억입니다.


남은 여유가 4천만 아래로 내려가면 경고가 납니다. 3백만이 되면 새 트랜잭션 아이디를 주지 않습니다. 쓰기가 막히고 읽기만 남습니다. 그 자리에서 풀 배큠이나 프리즈를 치면 아이디를 더 씁니다. 공식 순서는 준비된 트랜잭션, 긴 세션, 죽은 복제 슬롯을 치운 뒤 일반 배큠입니다. 예전처럼 싱글 유저로 서버를 내릴 필요는 보통 없습니다.


배치가 밤새 한 트랜잭션으로 주문 100만을 갈아엎으면, 그 스냅샷이 살아있는 동안 죽은 튜플을 못 걷습니다. 재고 락을 오래 잡는 자리는 분산 락 글과 겹칩니다. 여기는 락이 아니라 스냅샷이 배큠을 막는 자리입니다. 아웃박스가 논리 복제 슬롯을 붙잡는 밤은 아웃박스 글의 슬롯 적체와 같은 알람이 납니다.


데이터베이스와 테이블의 동결 나이를 본다
SELECT datname, age(datfrozenxid) AS xid_age FROM pg_database ORDER BY xid_age DESC; SELECT c.oid::regclass AS table_name, greatest(age(c.relfrozenxid), age(t.relfrozenxid)) AS xid_age FROM pg_class c LEFT JOIN pg_class t ON c.reltoastrelid = t.oid WHERE c.relkind IN ('r', 'm') ORDER BY xid_age DESC LIMIT 20;

알디에스에서 막힌 자리를 여는 순서

먼저 오토배큠이 돌고 있는지 보고, 다음에 긴 트랜잭션, 다음에 복제 슬롯을 봅니다. 워커가 안 보이면 파라미터가 꺼졌거나 통계 수집이 죽은 겁니다. 워커가 같은 큰 테이블만 붙잡고 있으면 나머지는 줄에서 기다립니다.


서울 리전 알디에스에서 제가 반복한 순서는 아래와 같습니다. 콘솔에서 autovacuum이 on인지, rds.adaptive_autovacuum이 on인지 확인합니다. 그다음 pg_stat_activity에서 autovacuum:로 시작하는 쿼리를 찾습니다. 랩어라운드 방지 배큠은 이름 끝에 (to prevent wraparound)가 붙고, 이 작업은 락이 와도 자동으로 끊기지 않습니다.


워커, 긴 스냅샷, 복제 슬롯을 같은 밤에 본다
SELECT pid, query, now() - xact_start AS xact_age FROM pg_stat_activity WHERE query LIKE 'autovacuum:%'; SELECT pid, usename, state, age(backend_xmin) AS xmin_age, left(query, 80) AS query FROM pg_stat_activity WHERE backend_xmin IS NOT NULL ORDER BY xmin_age DESC NULLS LAST LIMIT 10; SELECT slot_name, slot_type, active, xmin, catalog_xmin, restart_lsn FROM pg_replication_slots;

긴 세션은 커밋하거나 pg_terminate_backend로 끊습니다. 슬롯은 더 이상 없는 구독자용이면 삭제합니다. 디비지움이 붙어 있으면 슬롯을 함부로 지우지 않습니다. 스테이징에서 재생한 뒤에만 프로덕션에서 지웁니다. 그다음 문제 테이블에 일반 VACUUM (VERBOSE)를 칩니다. 랩어라운드가 임박한 밤에는 VACUUM FULLVACUUM FREEZE를 쓰지 않습니다.


애널라이즈만 반복하는 배치가 있으면 오토배큠이 셰어 업데이트 익스클루시브에서 밀립니다. 공식 문서가 경고하는 자리입니다. 파티션 부모는 자식이 바뀌어도 자동 애널라이즈가 안 돕니다. 부모를 사람이 주기적으로 분석해야 계획이 어긋나지 않습니다.


개발자가 데이터베이스 쿼리와 로그를 모니터에서 확인하는 장면
워커가 안 보이면 파라미터보다 긴 트랜잭션과 슬롯을 먼저 연다

저는 랩어라운드 알람을 디스크보다 먼저 본다

디스크는 카드로 키울 수 있습니다. 트랜잭션 아이디가 바닥나면 쓰기가 멈춥니다. 알디에스는 MaximumUsedTransactionIDsautovacuum_freeze_max_age와 5억 중 큰 값에 닿으면 적응형 오토배큠이 파라미터를 메모리에서 세게 밉니다. 파라미터 그룹 숫자는 그대로이고, 이벤트가 콘솔에 남습니다.


저는 클라우드워치에 이 메트릭을 1억 5천만과 3억에 두 줄 겁니다. 기본 동결 나이가 2억이라, 그 전에 슬롯과 긴 잡을 볼 시간이 생깁니다. 적응형을 끄면 그 밀기가 사라집니다. 알디에스는 켜 두라고 적습니다. 켜 둬도 랩어라운드는 가능합니다. 알람이 먼저입니다.


가상 스레드를 켠 스프링은 히카리 앞에서 디비를 더 자주 두드립니다. 커넥션 대기가 늘면 트랜잭션도 길어집니다. 그 밤은 히카리 글의 풀 표와 이 글의 스냅샷 표를 같이 봅니다. 온콜 예산은 에스엘오 글에 있습니다.


바쁜 주문 테이블만 스케일을 내린다
ALTER TABLE orders SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_vacuum_threshold = 50, autovacuum_analyze_scale_factor = 0.05 ); SHOW autovacuum; SHOW rds.adaptive_autovacuum; SHOW autovacuum_freeze_max_age;

인스턴스를 키우기 전에 채울 표

클래스를 올리기 전에 아래 다섯 칸이 비면, 돈만 나가고 기울기는 그대로입니다. 통과 기준은 제가 서울 리전 알디에스에서 반복한 숫자입니다. 팀 피크에 맞춰 조정합니다.


순서볼 것통과 기준
1죽은 튜플 비율, last_autovacuum바쁜 테이블 10% 미만. 시각이 피크 주기 안
2age(datfrozenxid), MaximumUsedTransactionIDs기본이면 1억 5천만 전에 알람
3긴 트랜잭션, backend_xmin배치가 한 스냅샷으로 밤새 안 남음
4복제 슬롯 xmin, catalog_xmin없는 구독자 슬롯 0. 디비지움은 지연만
5autovacuum, 적응형, 로그 최소 시간on / on. 로그로 워커가 보임

칸이 차면 그때 스토리지와 클래스를 봅니다. 히카리 최대 곱하기 인스턴스가 세션 한도를 넘기는 자리는 커넥션 풀 고갈입니다. 여기 표와 그 글의 표를 섞지 않습니다.


디스크 사용량과 트랜잭션 아이디 나이를 나란히 비교하는 정보 그림
인스턴스 클래스보다 죽은 튜플, 동결 나이, 슬롯 세 칸을 먼저 채운다

참고 자료


파라미터 기본값과 알디에스 적응형 문구는 수시로 바뀝니다. 위 표는 2026년 8월 공개 페이지 기준 점검용이며, 적용의 최종 근거는 공식 화면입니다.


자주 묻는 질문

오토배큠을 끄고 밤에만 배큠을 돌리면 되나?

예측 가능한 부하가 아니면 끄지 않습니다. 공식 문서는 데몬을 완전히 끄는 일을 권하지 않습니다. 꺼도 동결 나이가 한계를 넘으면 강제 배큠이 뜹니다. 밤 배치로 대부분을 처리하려면 데몬 임계만 높이고, 예기치 않은 대량 업데이트는 데몬이 받게 두는 타협이 있습니다.


디스크를 당장 줄이려면 풀 배큠이 답인가?

파일이 비고 한동안 안 커질 때만 후보입니다. 풀은 테이블을 잠그고 복사본만큼 용량이 잠깐 더 필요합니다. 매일 업데이트되는 주문 테이블은 일반 배큠을 자주 쳐서 재사용 자리를 유지하는 편이 맞습니다. 피크에 풀을 치면 락 대기만 깁니다.


랩어라운드 경고가 뜨면 서버를 내려야 하나?

요즘은 보통 내리지 않습니다. 준비된 트랜잭션, 긴 세션, 죽은 복제 슬롯을 치운 뒤 일반 배큠을 칩니다. 풀과 프리즈는 아이디를 더 쓰므로 임박한 밤에는 피합니다. 쓰기가 이미 막혀도 배큠은 돌아갑니다.


알디에스에서 적응형 오토배큠을 꺼도 되나?

끄지 않는 편이 맞습니다. 알디에스는 켜 두라고 적습니다. 켜 둬도 랩어라운드는 가능하므로 클라우드워치 MaximumUsedTransactionIDs 알람은 따로 겁니다. 적응형은 파라미터 그룹 숫자를 바꾸지 않고 메모리에서만 세게 밉니다.


파티션 테이블은 오토배큠이 부모를 분석하나?

하지 않습니다. 자식이 바뀌어도 부모 애널라이즈는 자동이 아닙니다. 부모를 사람이 주기적으로 분석해야 계획이 어긋나지 않습니다. 파티션 자체는 일반 테이블처럼 워커가 돕니다.


죽은 튜플이 많은 테이블만 스케일을 내려도 되나?

됩니다. ALTER TABLE SET으로 테이블마다 스케일과 임계를 줍니다. 바쁜 주문만 0.05로 내리고 설정 테이블은 기본값을 유지합니다. 전역 스케일만 내리면 작은 테이블까지 워커가 바빠집니다.


오토배큠죽은 튜플트랜잭션 ID랩어라운드포스트그레PostgreSQL알디에스백엔드API클라우드개발자데이터

함께 보면 좋은 문제 해결

EXPLORE / Backend

이어서 읽어보기

전체 토픽 둘러보기