如何保持 Postgres 佇列的健康與效能
如何保持 Postgres 佇列的健康與效能。
Postgres 雖非專為佇列設計,但常被用於處理混合工作負載。本文探討如何透過管理 MVCC 與清理機制,解決佇列效能退化問題,並強調在混合環境下維持系統健康的重要性。
Postgres 佇列的運作機制
許多開發者選擇將 Postgres 作為佇列使用,主要優勢在於能將工作狀態與應用程式邏輯保持在同一交易中,確保資料一致性。
- 透過
FOR UPDATE SKIP LOCKED語法,開發者可以實現併發處理,確保多個工作者 (Worker) 不會重複執行相同的任務。 - 這種模式的核心在於:工作者呼叫交易、取得任務、執行工作,成功後刪除該列並提交交易;若失敗則回滾,任務將重新變為可見。
效能殺手:死元組 (Dead Tuples) 與清理機制
Postgres 的多版本併發控制 (MVCC) 設計意味著 DELETE 操作不會立即移除資料,而是將其標記為「死元組」。這些死元組會導致嚴重的效能隱患:
- 堆疊掃描 (Heap scan):執行器必須讀取死元組並檢查其可見性,這會增加不必要的 I/O。
- 索引掃描 (Index scan):B-tree 索引會累積指向死元組的參考,導致掃描時遍歷無效指標,造成額外開銷。
- 若資料庫無法在死元組累積的速度下完成清理 (Vacuum),效能將顯著下降,甚至拖垮整個資料庫。
混合工作負載帶來的清理瓶頸
在混合工作負載環境中,佇列效能往往受限於其他並行運行的查詢。自動清理 (Autovacuum) 無法移除仍對活躍交易可見的死元組,這導致了常見的失敗模式:
- 長時間運行的交易會鎖定 MVCC 水平線 (Horizon),導致清理機制無法運作。
- 即使是多個短暫但重疊的分析查詢,也可能持續鎖定該水平線,導致清理停滯。
- 問題的根源不在於 Postgres 不適合做佇列,而在於當佇列與其他高資源消耗的查詢共存時,缺乏有效的資源隔離與流量控制。
優化策略與流量控制
傳統的逾時設定(如 statement_timeout 或 transaction_timeout)屬於較粗糙的手段,無法解決併發資源爭用的問題。針對此類場景,PlanetScale 提出了「Database Traffic Control™」:
- 該工具允許針對不同流量類別進行細粒度控制,為特定查詢設定資源預算。
- 當查詢超過資源限制時,系統可將其阻擋,從而確保高優先級任務不受影響,並讓自動清理機制有機會運作。
- 關鍵提醒:採用此類控制手段時,應用程式必須具備完善的重試邏輯,以應對被阻擋的查詢,確保系統在平滑處理負載的同時,維持整體穩定性。
Postgres wasn't designed as a job queue, but many apps use it as one right alongside analytics, OLTP, and everything else.
— PlanetScale (@PlanetScale) April 10, 2026
Our latest blog goes deep on what to be aware of when doing this and how the situation has improved for Postgres over time. pic.twitter.com/lriga8zYA5
— PlanetScale (@PlanetScale) April 10, 2026
