騰訊雲帳號認證充值 騰訊雲 PostgreSQL 實例 Autovacuum 阻塞導致磁碟空間暴漲排查
騰訊雲帳號認證充值 背景與現象:Autovacuum 被卡住,磁碟狂漲
某天清晨,騰訊雲 PostgreSQL 生產實例突然觸發磁碟告警,短短幾個小時從 60% 漲到 95%。CPU 與連線數並無異常,但 WAL 目錄與幾張熱表所在的資料目錄膨脹明顯。登入一看,autovacuum 程序大量存在,卻長時間無進展。這種場景的本質,多半是 autovacuum 被阻塞,無法及時回收死行與可重用空間,連帶引發 WAL 堆積與資料檔案膨脹。
騰訊雲帳號認證充值 本文從機制到實操,給出一套面向騰訊雲 PostgreSQL 的排查路徑,並整理常見阻塞場景、止血手段與調優策略,幫你既能快速解決當前危機,也能避免同類事故反覆上演。
Autovacuum 機制簡述:為何它一旦卡住就會惡化
PostgreSQL 透過 MVCC 保證並發,刪除或更新不會立刻回收舊版本行,只有在所有可能可見該版本的事務都結束後,VACUUM 才能清理死行、釋放可重用空間,並更新可見性對映以降低隨後查詢成本。這項例行工作主要由 autovacuum 背景程序負責。
騰訊雲帳號認證充值 當 autovacuum 被長事務、鎖或配置閾值限制住,死行得不到回收,表與索引持續膨脹;同時,為保持一致性,系統產生並保留更多 WAL,遇上複製槽積壓更會加劇磁碟壓力。若拖得更久,還可能逼近 XID wraparound 風險,系統會強制優先 freeze,對線上負載形成更大擾動。
騰訊雲環境的幾個特點
雲上實例通常具備更完善的監控與告警,包含磁碟、IOPS、WAL 產生速率等;部分參數可透過控制台或工單調整,但原理與社群版 PostgreSQL 一致。排障時可依賴系統視圖與日誌,不必拘泥於特定雲廠商差異。本文的 SQL 與步驟對雲上與自建均適用。
騰訊雲帳號認證充值 排查步驟總覽
建議由外而內、由粗到細,快速定位阻塞點與罪魁禍首。
1. 觀察磁碟與 WAL 增長
先確認磁碟與 WAL 是否同時增長。若 WAL 快速攀升,關注複製槽與訂閱是否積壓;若主要是資料檔案增長,關注表索引膨脹與 autovacuum 落後。
-- 近似觀察 WAL 產出速率(以 LSN 差估算)
select now() as ts,
pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0') as wal_bytes_since_zero;
-- 複製槽保留的 WAL 量(如有使用)
select slot_name, active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) as retained
from pg_replication_slots;
2. 查 autovacuum 啟動與等待狀態
確認 autovacuum 是否啟用、是否有工作緒在跑、以及在等待什麼。
show autovacuum;
show track_activities;
-- 觀察 autovacuum 工作緒與等待事件
select pid, usename, state, wait_event_type, wait_event,
xact_start, now() - xact_start as xact_age,
query
from pg_stat_activity
where query like '%autovacuum%'
order by xact_start nulls last;
-- autovacuum 是否被鎖住
select a.pid, a.query, l.locktype, l.relation::regclass, l.mode, l.granted
from pg_locks l
join pg_stat_activity a on a.pid = l.pid
where a.query like '%autovacuum%';
3. 檢查長事務與『idle in transaction』
長事務會抬高 OldestXmin,使 autovacuum 難以清理死行,形成表面上『在跑但不見進展』的假象。特別要盯住長時間 idle in transaction 的連線。
select pid, usename, state, xact_start, now()-xact_start as age,
wait_event_type, wait_event, query
from pg_stat_activity
where state in ('active','idle in transaction')
order by xact_start nulls last
limit 50;
4. 找出膨脹表與索引
優先關注死行多、尺寸突增的熱表與索引,定位清理收益最大者。
-- 死行排名
select relname, n_dead_tup, last_vacuum, last_autovacuum
from pg_stat_user_tables
order by n_dead_tup desc
limit 20;
-- 表總大小(含索引與 TOAST)
select relname,
pg_size_pretty(pg_total_relation_size(relid)) as total_size
from pg_catalog.pg_statio_user_tables
order by pg_total_relation_size(relid) desc
limit 20;
-- 索引大小
select schemaname, relname, indexrelname,
pg_size_pretty(pg_relation_size(indexrelid)) as idx_size
from pg_stat_user_indexes
order by pg_relation_size(indexrelid) desc
limit 20;
5. 檢查表的 autovacuum 閾值
若表很大但 autovacuum 閾值偏高,啟動將被延後。計算規則:vacuum_threshold = autovacuum_vacuum_threshold + reltuples * autovacuum_vacuum_scale_factor。
show autovacuum_vacuum_threshold; -- 預設 50
show autovacuum_vacuum_scale_factor; -- 常見 0.2
-- 估算某表是否已達閾值
select relname, reltuples::bigint as est_rows,
current_setting('autovacuum_vacuum_threshold')::int as base,
current_setting('autovacuum_vacuum_scale_factor')::float as scale,
(current_setting('autovacuum_vacuum_threshold')::int
+ reltuples * current_setting('autovacuum_vacuum_scale_factor')::float) as trigger
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relkind = 'r'
order by reltuples desc
limit 10;
6. 觀察凍結年齡與 wraparound 風險
若 age(datfrozenxid) 過大,系統會更積極地觸發 freeze,與業務操作疊加容易引發 IO 抖動。
select datname, age(datfrozenxid) as xid_age
from pg_database
order by xid_age desc;
show autovacuum_freeze_max_age; -- 常見數值數億級
show vacuum_freeze_table_age;
7. 排查複製槽與邏輯訂閱
若有複製槽長時間不消費,WAL 無法釋放,磁碟會被 WAL 撐滿。雲上遷移、日誌採集、資料匯入常見此情況。
select slot_name, slot_type, active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) as retained,
restart_lsn
from pg_replication_slots
order by pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) desc;
典型阻塞場景與解法
場景一:idle in transaction 長時間佔著舊快照
某應用在事務中打開連線後不提交,保持 idle in transaction 幾小時。autovacuum 雖然啟動,但因 OldestXmin 過於陳舊,多數死行無法被回收,只能不斷掃描與重試,徒增 IO 與 WAL。
處理步驟:
- 立刻終止過久的 idle in transaction 連線,或與業務協調先行提交/回滾。
- 設置保護性超時:statement_timeout、idle_in_transaction_session_timeout,避免再次出現。
- 在非高峰期對受影響大表執行 VACUUM,必要時 REINDEX 或 VACUUM FULL(謹慎)。
-- 緊急清理:先殺掉最老的 idle in transaction
select pg_terminate_backend(pid)
from pg_stat_activity
where state = 'idle in transaction' and now()-xact_start > interval '15 minutes';
alter role appuser set idle_in_transaction_session_timeout = '5min';
alter database mydb set statement_timeout = '30s';
場景二:大批量更新與多索引熱表
對熱表進行長時間批量更新,且索引較多。更新會導致行版本暴增與索引膨脹;autovacuum 受 cost 限制與鎖等待影響,回收速度跟不上寫入,磁碟一路上漲。
解法要點:
- 騰訊雲帳號認證充值 調整 autovacuum 的成本與併發,提升清理吞吐:降低 autovacuum_vacuum_cost_delay、提高 autovacuum_vacuum_cost_limit、增大 maintenance_work_mem。
- 將熱表的 scale factor 下調為絕對值觸發,避免大表啟動過晚:對單表設置 autovacuum_vacuum_scale_factor 為 0.01 或更小,並設置 autovacuum_vacuum_threshold。
- 批量更新改為分批,或者先建立新表導入後原子切換,降低長時間寫放大。
- 針對嚴重膨脹的索引,在低峰期 REINDEX CONCURRENTLY。
alter table public.orders set (
autovacuum_vacuum_scale_factor = 0.01,
autovacuum_vacuum_threshold = 5000
);
set maintenance_work_mem = '2GB'; -- 臨時會話級
set autovacuum_vacuum_cost_limit = 2000; -- 需在參數層或每表調整
場景三:一次性大事務或重寫表
一次性巨量刪改或 CTAS/重建操作,短時間內生成大量死行與 WAL。若同時存在長讀或慢消費的下游,回收與釋放延後,磁碟暴漲更明顯。
解法要點:
- 將大事務拆分為小批次,並在批次間插入短暫休眠,給 autovacuum 可跟進的時間窗口。
- 對歷史分區優先做 DROP/DETACH,而非大規模 DELETE。
- 必要時對巨表安排 VACUUM FULL 逐表滾動,但需預留兩倍空間與停寫窗口。
場景四:複製槽積壓導致 WAL 撐盤
某次異地訂閱中斷,物理或邏輯複製槽長時間不消費。主庫為保證可恢復,持續保留舊 WAL,磁碟逐步被佔滿。這與 autovacuum 表膨脹可以同時存在,形成雙重壓力。
解法要點:
- 恢復下游消費,或臨時提升網路帶寬與同步併發。
- 對無主體需求的槽評估後刪除或重建。
- 在確保恢復策略下調整 wal_keep_size、歸檔策略與清理週期。
-- 謹慎刪除無用槽
select pg_drop_replication_slot('stale_slot');
場景五:autovacuum 門檻與工作緒不足
全局 scale factor 偏大、autovacuum_max_workers 偏小、naptime 偏長,會讓大表或多表同時積壓時難以及時啟動與覆蓋。雲上高峰寫入特別容易踩雷。
解法要點:
- 提高 autovacuum_max_workers、autovacuum_naptime 調短、autovacuum_vacuum_cost_limit 調高。
- 為核心熱表設置 per-table 的更激進策略,避免被全局預設稀釋。
- 在具體版本上啟用 autovacuum_work_mem(如支援),避免受限於過小的 maintenance_work_mem。
臨時止血:如何安全釋放磁碟
優先順序與原則
止血目標是盡快阻止惡化並回收可釋放空間,原則是『先解除阻塞,再做清理』,避免無效掃描與加劇 IO 抖動。
- 第一步:終止最老的長事務與 idle in transaction。
- 第二步:觀察 WAL 是否因複製槽保留,如是先處理槽。
- 第三步:針對死行最多且使用頻繁的幾張表,優先手動 VACUUM。
- 第四步:在低峰對嚴重膨脹索引 REINDEX CONCURRENTLY。
VACUUM、REINDEX、VACUUM FULL 的取捨
普通 VACUUM 釋放的是可重用空間,檔案不縮小,但能降低後續膨脹速度;REINDEX 可回收索引膨脹;VACUUM FULL 能縮檔,但需要獨佔鎖與額外空間,會對線上有明顯影響,僅在其他手段效果有限時使用。對極大表,可考慮分區重建與原子切換。
清理歷史資料與分區管理
對時間序列或日誌類資料,最有效的減壓方式是定期 DROP 舊分區;相比大批量 DELETE,DROP 幾乎瞬時釋放空間,且 WAL 量更小。若尚未分區,盡快在安全窗口完成分區改造。
參數調優建議
Autovacuum 相關
- autovacuum = on(必開)
- autovacuum_max_workers:視核心數與表數量提到 5-10,避免一兩個工人扛全場。
- autovacuum_naptime:縮短到 10s-30s,縮小觸發延遲。
- autovacuum_vacuum_threshold:100-1000,避免大表觸發過晚。
- autovacuum_vacuum_scale_factor:0.01-0.05,熱表個別更小。
- autovacuum_analyze_scale_factor:0.05-0.1,保證統計資訊新鮮。
- autovacuum_vacuum_cost_limit:1000-3000,配合成本延遲。
- autovacuum_vacuum_cost_delay:1ms-5ms,忙時可暫降。
- autovacuum_work_mem(若支援):設為 1GB-2GB,否則提升 maintenance_work_mem。
維護與檢查點
- maintenance_work_mem:1GB-4GB(依記憶體),加速 VACUUM 與重建索引。
- max_parallel_maintenance_workers:2-4,加快重建索引等任務。
- checkpoint_timeout、max_wal_size:平衡檢查點頻率與 WAL 堆積,避免過於頻繁或過於巨大。
針對熱表的局部策略
不要只改全局。對流量峰值高、更新密集的表單獨設置更激進的參數:
alter table public.hot_table set (
autovacuum_vacuum_scale_factor = 0.005,
autovacuum_vacuum_threshold = 1000,
autovacuum_analyze_scale_factor = 0.02
);
必要時對 TOAST 設置相同策略,避免大欄位造成回收延遲。
監控與預防
關鍵指標
- 磁碟用量、WAL 產生速率與保留量。
- autovacuum lag:死行總量、last_autovacuum 時間分佈。
- 長事務數量與最老年齡。
- 複製槽 retained bytes。
- xid age 與凍結進度。
-- 長事務快照
select count(*) filter (where now()-xact_start > interval '5 min') as tx_over_5m,
max(now()-xact_start) as oldest
from pg_stat_activity
where state in ('active','idle in transaction');
-- 表級 autovacuum 最近一次執行情況
select relname, n_dead_tup, last_autovacuum
from pg_stat_user_tables
order by last_autovacuum nulls first, n_dead_tup desc
limit 50;
制度與開發側改進
- 為關鍵連線賦予 idle_in_transaction_session_timeout,防止『忘記提交』。
- 對批處理作業制定分批與節流策略,控制每批行數與間隔。
- 在變更評審中審視是否對熱表做大規模刪改或重寫,必要時安排專門窗口。
- 建立月度膨脹盤點,對增長最快的表索引安排維護。
實戰案例(泛化)
一個交易系統,核心訂單表 5 億行,每日高峰更新密集。某次促銷後,磁碟從 70% 飆至 92%。排查發現:
- 多個應用節點出現 idle in transaction,最老持續 3 小時。
- orders 表 n_dead_tup 破 1.2 億,last_autovacuum 已滯後 8 小時。
- 兩個邏輯複製槽其中一個下游暫停,retained WAL 逼近 120GB。
騰訊雲帳號認證充值 處置與效果:
- 立即終止最老 idle in transaction,恢復下游消費。
- 騰訊雲帳號認證充值 臨時提高 autovacuum_max_workers 至 8,cost_limit 提至 2000,naptime 調成 10s。
- 對 orders 分批 VACUUM,對兩個最大索引 REINDEX CONCURRENTLY。
- 在低峰將 orders 設定 autovacuum_vacuum_scale_factor=0.005,threshold=2000。
4 小時後,WAL 產生速率回落,retained 降至 5GB;24 小時後 orders 表死行下降 80% 以上,磁碟回到 76%。事後新增了超時與批處理節流,之後再未出現同類暴漲。
結語
Autovacuum 是 PostgreSQL 穩定運行的基石,真正的風險不在於它『慢』,而在於它『被卡』。一旦被長事務、鎖或參數閾值限制住,死行與 WAL 就會反向拉動磁碟暴漲,越晚處理越難收拾。雲上環境給了我們更便利的監控與調參能力,但根本之道仍是:規範事務、合理分批、針對熱表單獨策略、建立可觀測與巡檢體系。把這些做好,Autovacuum 就會如同默默工作的清道夫,讓你的騰訊雲 PostgreSQL 在高壓業務下依舊平穩運行。

