2025年9月25日: PostgreSQL 18 釋出!
支援的版本:當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 7.3 / 7.2

24.1. 常規清理 #

PostgreSQL 資料庫需要稱為清理的定期維護。對於許多安裝來說,由自動清理守護程序執行清理就足夠了,該守護程序在第 24.1.6 節中進行了描述。您可能需要調整其中描述的自動清理引數,以獲得針對您情況的最佳結果。一些資料庫管理員可能希望透過手動管理的 VACUUM 命令來補充或替代守護程序的活動,這些命令通常會根據計劃由 cronTask Scheduler 指令碼執行。要正確設定手動管理的清理,理解接下來的幾個小節中討論的問題至關重要。依賴自動清理的管理員可能仍希望快速瀏覽這些材料,以幫助他們理解和調整自動清理。

24.1.1. 清理基礎知識 #

PostgreSQLVACUUM 命令需要定期處理每個表,原因如下:

  1. 回收或重用已更新或刪除的行佔用的磁碟空間。
  2. 更新 PostgreSQL 查詢最佳化器使用的統計資訊。
  3. 更新可見性圖,該圖可加速僅索引掃描
  4. 防止因事務 ID 迴繞多事務 ID 迴繞而丟失非常舊的資料。

這些原因中的每一個都決定了執行不同頻率和範圍的 VACUUM 操作,如以下小節所述。

VACUUM 有兩個變體:標準 VACUUMVACUUM FULLVACUUM FULL 可以回收更多磁碟空間,但執行速度慢得多。此外,標準形式的 VACUUM 可以與生產資料庫操作並行執行。(諸如 SELECTINSERTUPDATEDELETE 等命令將繼續正常工作,但當表被清理時,您將無法使用諸如 ALTER TABLE 等命令修改表的定義。) VACUUM FULL 需要對正在處理的表進行 ACCESS EXCLUSIVE 鎖,因此無法與其他表的使用並行進行。因此,通常情況下,管理員應努力使用標準 VACUUM 並避免 VACUUM FULL

VACUUM 會產生大量的 I/O 流量,這可能導致其他活動會話的效能下降。有一些配置引數可以調整以減少後臺清理的效能影響 — 請參閱第 19.10.2 節

24.1.2. 回收磁碟空間 #

PostgreSQL 中,行的 UPDATEDELETE 操作不會立即刪除行的舊版本。此方法對於獲得多版本併發控制 (MVCC) 的好處是必需的(MVCC參見第 13 章):只要行版本仍然可能對其他事務可見,就不能刪除它。但最終,過時或已刪除的行版本將不再被任何事務關注。然後必須回收它佔用的空間以供新行重用,以避免磁碟空間需求的無限增長。這透過執行 VACUUM 來完成。

標準形式的 VACUUM 會移除表和索引中的死行版本,並將空間標記為可供將來重用。但是,它不會將空間返回給作業系統,除非在一種特殊情況下,即表中一個或多個頁面完全變為空閒,並且可以輕鬆獲取排他性表鎖。相反,VACUUM FULL 透過寫入一個沒有死空間的完整的新表文件版本來主動壓縮表。這最小化了表的大小,但可能需要很長時間。它還為新表副本需要額外的磁碟空間,直到操作完成。

例行清理的通常目標是足夠頻繁地執行標準 VACUUM 操作,以避免需要 VACUUM FULL。自動清理守護程序試圖以這種方式工作,事實上,它永遠不會發出 VACUUM FULL。在此方法中,其思想不是將表保持在其最小尺寸,而是保持穩定的磁碟空間使用:每個表佔用的空間相當於其最小尺寸加上清理執行之間使用的空間。雖然 VACUUM FULL 可用於將表縮小回其最小尺寸並向作業系統返回磁碟空間,但如果表將來會再次增長,那麼這樣做意義不大。因此,對於維護大量更新的表,中等頻率的標準 VACUUM 執行比不頻繁的 VACUUM FULL 執行更好。

一些管理員更喜歡自己安排清理,例如在低負載的夜間完成所有工作。根據固定時間表進行清理的困難在於,如果一個表意外地更新活動激增,它可能會變得腫脹到實際上需要 VACUUM FULL 來回收空間。使用自動清理守護程序可以緩解這個問題,因為守護程序會根據更新活動動態地安排清理。除非您有高度可預測的工作負載,否則完全停用守護程序是不明智的。一種可能的折衷是設定守護程序的引數,使其僅對異常重的更新活動做出響應,從而防止事情失控,而計劃的 VACUUM 操作則負責在典型負載下完成大部分工作。

對於不使用自動清理的管理員來說,一種典型的方法是在低使用期安排每天一次資料庫範圍的 VACUUM,並根據需要補充對大量更新的表的更頻繁的清理。(一些更新率極高的安裝會像每隔幾分鐘一樣頻繁地清理其最繁忙的表。)如果您在一個叢集中有多個數據庫,請不要忘記 VACUUM 每個資料庫;程式 vacuumdb 可能會有所幫助。

提示

當一個表因大量更新或刪除活動而包含大量死行版本時,普通的 VACUUM 可能不令人滿意。如果您有這樣的表並且需要回收它佔用的過多磁碟空間,您將需要使用 VACUUM FULL,或者改用 CLUSTERALTER TABLE 的某個表重寫變體。所有這些命令都會重寫表的一個全新副本併為其構建新索引。所有這些選項都需要 ACCESS EXCLUSIVE 鎖。請注意,在操作完成之前,舊錶和索引副本無法被釋放,因此它們也會暫時使用大約等於表大小的額外磁碟空間。

提示

如果您有一個表在其全部內容被定期刪除,請考慮使用 TRUNCATE 而不是使用 DELETE 後跟 VACUUMTRUNCATE 會立即刪除表的所有內容,而無需後續的 VACUUMVACUUM FULL 來回收現在未使用的磁碟空間。缺點是嚴格的 MVCC 語義被違反了。

24.1.3. 更新最佳化器統計資訊 #

為了生成良好的查詢計劃,PostgreSQL 查詢最佳化器依賴於表內容的統計資訊。這些統計資訊由 ANALYZE 命令收集,該命令可以單獨呼叫,也可以作為 VACUUM 的可選步驟。擁有相對準確的統計資訊很重要,否則糟糕的計劃選擇可能會降低資料庫效能。

如果啟用了自動清理守護程序,它將在表內容發生足夠大的變化時自動發出 ANALYZE 命令。但是,管理員可能更喜歡依賴手動計劃的 ANALYZE 操作,特別是當已知對錶的更新活動不會影響“有趣”列的統計資訊時。守護程序嚴格地將 ANALYZE 的計劃安排為插入或更新的行數的一個函式;它不知道這是否會導致有意義的統計變化。

分割槽和繼承子表中的元組更改不會觸發父表的分析。如果父表為空或很少更改,它可能永遠不會被自動清理處理,並且繼承樹整體的統計資訊也不會被收集。因此,有必要手動執行 ANALYZE 來保持統計資訊最新。

與清理空間回收一樣,對於大量更新的表,頻繁更新統計資訊比很少更新的表更有用。但即使對於大量更新的表,如果資料的統計分佈變化不大,也可能不需要更新統計資訊。一個簡單的經驗法則是考慮表中列的最小值和最大值變化的程度。例如,包含行更新時間的 timestamp 列將隨著行的新增和更新而具有不斷增加的最大值;這樣的列可能比包含網站訪問頁面 URL 的列需要更頻繁的統計資訊更新。URL 列可能接收相同的更改頻率,但其值的統計分佈可能變化相對緩慢。

可以對特定表甚至表的特定列執行 ANALYZE,因此如果您的應用程式需要,可以提供更頻繁地更新某些統計資訊的靈活性。但在實踐中,最好只分析整個資料庫,因為它是一個快速操作。ANALYZE 使用表中行的統計隨機樣本,而不是讀取每一行。

提示

雖然對 ANALYZE 頻率進行逐列調整可能效果不大,但您可能會發現逐列調整 ANALYZE 收集的統計資訊詳細程度是值得的。在 WHERE 子句中大量使用且資料分佈高度不規則的列可能比其他列需要更精細的資料直方圖。請參閱 ALTER TABLE SET STATISTICS,或使用 default_statistics_target 配置引數更改資料庫範圍的預設值。

此外,預設情況下,關於函式選擇性的資訊有限。但是,如果您建立了使用函式呼叫的統計物件或表示式索引,將收集關於該函式的資訊,這可以極大地改進使用表示式索引的查詢計劃。

提示

自動清理守護程序不會對外部表發出 ANALYZE 命令,因為它無法確定何時可能有用。如果您的查詢需要外部表的統計資訊來進行正確規劃,最好按照合適的計劃手動執行這些表的 ANALYZE 命令。

提示

自動清理守護程序不會對分割槽表發出 ANALYZE 命令。僅當父表本身發生更改時,繼承父表才會被分析 — 子表的更改不會觸發父表的自動分析。如果您的查詢需要父表的統計資訊來進行正確規劃,有必要定期對手動執行這些表的 ANALYZE 以保持統計資訊最新。

24.1.4. 更新可見性圖 #

清理程式為每個表維護一個可見性圖,以跟蹤哪些頁面僅包含已知對所有活動事務(以及直到頁面再次被修改之前的所有未來事務)可見的元組。這有兩個目的。首先,清理程式本身可以在下一次執行時跳過這些頁面,因為沒有什麼需要清理。

其次,它允許 PostgreSQL 僅使用索引回答某些查詢,而不引用底層表。由於 PostgreSQL 索引不包含元組可見性資訊,因此正常索引掃描會為每個匹配的索引條目獲取堆元組,以檢查它是否應該被當前事務看到。另一方面,僅索引掃描會先檢查可見性圖。如果已知頁面上的所有元組都可見,則可以跳過堆提取。這在可見性圖可以防止磁碟訪問的大型資料集上非常有用。可見性圖遠小於堆,因此即使堆非常大,它也可以輕鬆快取。

24.1.5. 防止事務 ID 迴繞失敗 #

PostgreSQLMVCC事務語義依賴於能夠比較事務 ID(XID)編號:插入 XID 大於當前事務 XID 的行版本是“未來”的,並且不應對當前事務可見。但是,由於事務 ID 的大小有限(32 位),長時間執行(超過 40 億次事務)的叢集將遭受事務 ID 迴繞:XID 計數器迴繞到零,並且突然間曾經過去的事務現在看起來像是未來的事務——這意味著它們的結果變得不可見。簡而言之,災難性的資料丟失。(實際上資料仍然存在,但如果您無法訪問它,那也無濟於事。)為了避免這種情況,必須至少每 20 億次事務清理每個資料庫中的每個表。

定期清理可以解決問題的原因為 VACUUM 會將行標記為凍結,表示它們是由已提交的足夠遠的事務插入的,以至於插入事務的效果肯定對所有當前和未來的事務都可見。正常 XID 使用模 232 算術進行比較。這意味著對於每個正常 XID,都有 20 億個 XID 比它“”,另外 20 億個 XID 比它“”;換句話說,正常 XID 空間是迴圈的,沒有端點。因此,一旦建立了一個具有特定正常 XID 的行版本,該行版本在接下來的 20 億次事務中都會顯得“過去”,無論我們指的是哪個正常 XID。如果該行版本在超過 20 億次事務後仍然存在,它將突然顯得“未來”。為了防止這種情況,PostgreSQL 保留了一個特殊的 XID,FrozenTransactionId,它不遵循正常的 XID 比較規則,並且始終被認為比任何正常 XID 都舊。凍結的行版本被視為好像插入 XID 是 FrozenTransactionId,因此無論它們存在多久,它們對所有正常事務都會顯得“過去”,並且不受迴繞問題的影響。因此,這些行版本將保持有效,直到被刪除,無論那需要多久。

注意

PostgreSQL 9.4 之前的版本中,透過實際用 FrozenTransactionId 替換行的插入 XID 來實現凍結,這在行的 xmin 系統列中可見。較新版本僅設定一個標誌位,保留行的原始 xmin 以供可能的取證使用。但是,在從 9.4 之前的版本升級的資料庫中,仍然可以找到 xmin 等於 FrozenTransactionId (2) 的行。

此外,系統目錄可能包含 xmin 等於 BootstrapTransactionId (1) 的行,表示它們是在 initdb 的第一個階段插入的。與 FrozenTransactionId 一樣,這個特殊的 XID 被認為比任何正常 XID 都舊。

vacuum_freeze_min_age 控制 XID 值需要多舊才能凍結帶有該 XID 的行。增加此設定可能會避免不必要的工作,如果原本要凍結的行很快就會被修改,但減少此設定會增加表在必須再次清理之前可以經過的事務數。

VACUUM 使用可見性圖來確定必須掃描表的哪些頁面。通常,即使這些頁面可能仍包含具有舊 XID 值的行版本,它也會跳過沒有任何死行版本的頁面。因此,常規 VACUUM 不會始終凍結表中的所有舊行版本。當這種情況發生時,VACUUM 最終需要執行強制清理,它將凍結所有符合條件的未凍結 XID 和 MXID 值,包括那些來自全部可見但並非全部凍結的頁面。

如果一個表正在累積一個所有頁面都可見但並非所有頁面都凍結的積壓,一個常規清理可能會選擇掃描可跳過的頁面以嘗試凍結它們。這樣做會減少下一次強制清理必須掃描的頁面數量。這些被稱為急切掃描的頁面。可以透過增加 vacuum_max_eager_freeze_failure_rate 來調整急切掃描,以嘗試凍結更多所有頁面都可見的頁面。即使急切掃描已將所有頁面可見但並非全部凍結的頁面數量降至最低,大多數表仍需要定期強制清理。但是,任何成功急切凍結的頁面都可以在強制清理期間被跳過,因此急切凍結可能會最小化強制清理的開銷。

vacuum_freeze_table_age 控制何時強制清理一個表。如果自上次此類掃描以來經過的事務數大於 vacuum_freeze_table_age 減去 vacuum_freeze_min_age,則掃描所有頁面可見但並非全部凍結的頁面。將 vacuum_freeze_table_age 設定為 0 會強制 VACUUM 始終使用其強制策略。

一個表可以不被清理的最長時間是 20 億個事務減去上次強制清理時的 vacuum_freeze_min_age 值。如果它超過這個時間未被清理,可能會導致資料丟失。為了確保這種情況不會發生,當資料庫中最舊的 XID 距離迴繞點只有 autovacuum_freeze_max_age 事務時,就會自動呼叫 autovacuum。 (即使 autovacuum 被停用,這也會發生。)

這意味著,如果一個表沒有被其他方式清理,它將在大約每 autovacuum_freeze_max_age 減去 vacuum_freeze_min_age 個事務後呼叫 autovacuum。對於定期因空間回收而清理的表來說,這無關緊要。然而,對於靜態表(包括接收插入但沒有或幾乎沒有更新或刪除操作的表),則不需要為了回收空間而進行清理,因此嘗試最大化強制 autovacuum 對非常大的靜態表的間隔是有益的。顯然,可以透過增加 autovacuum_freeze_max_age 或減少 vacuum_freeze_min_age 來實現這一點。

vacuum_freeze_table_age 的有效最大值是 0.95 * autovacuum_freeze_max_age;大於此值的設定將被限制為最大值。大於 autovacuum_freeze_max_age 的值沒有意義,因為到那時將觸發一個反迴繞 autovacuum,並且 0.95 的乘數留有一些緩衝空間,可以在此之前執行手動 VACUUM。經驗法則規定,vacuum_freeze_table_age 應設定為略低於 autovacuum_freeze_max_age 的值,留出足夠的間隔,以便一個定期計劃的 VACUUM 或由正常刪除和更新活動觸發的 autovacuum 在此視窗中執行。將其設定得太近可能會導致反迴繞 autovacuum,即使表最近被清理以回收空間,而較低的值會導致更頻繁的強制清理。

增加 autovacuum_freeze_max_age (以及 vacuum_freeze_table_age) 的唯一缺點是 pg_xactpg_commit_ts 子目錄的資料庫叢集將佔用更多空間,因為它必須儲存從 autovacuum_freeze_max_age 範圍回溯到所有事務的提交狀態(如果啟用了 track_commit_timestamp)和時間戳。提交狀態每個事務使用兩位,因此如果 autovacuum_freeze_max_age 設定為其允許的最大值 20 億,那麼 pg_xact 預計會增長到大約半個 GB,而 pg_commit_ts 會增長到大約 20GB。如果這與您的總資料庫大小相比微不足道,則建議將 autovacuum_freeze_max_age 設定為其允許的最大值。否則,根據您願意為 pg_xactpg_commit_ts 儲存分配多少空間來設定它。(預設值 2 億事務,相當於大約 50MB 的 pg_xact 儲存和大約 2GB 的 pg_commit_ts 儲存。)

減少 vacuum_freeze_min_age 的一個缺點是它可能會導致 VACUUM 做無用功:如果行很快被修改(導致它獲得新的 XID),那麼凍結行版本就是浪費時間。因此,設定應該足夠大,以至於行不太可能再次更改。

為了跟蹤資料庫中最舊未凍結 XID 的年齡,VACUUM 將 XID 統計資訊儲存在系統表 pg_classpg_database 中。特別是,表 pg_class 行的 relfrozenxid 列包含在上次成功推進 relfrozenxidVACUUM(通常是上次強制 VACUUM)結束時仍然存在的​​最舊未凍結 XID。類似地,資料庫 pg_database 行的 datfrozenxid 列是該資料庫中出現的未凍結 XID 的下界 — 它只是資料庫中每個表 relfrozenxid 值的最小值。檢查此資訊的便捷方法是執行如下查詢:

SELECT c.oid::regclass as table_name,
       greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm');

SELECT datname, age(datfrozenxid) FROM pg_database;

age 列測量從截止 XID 到當前事務 XID 的事務數。

提示

當指定 VACUUM 命令的 VERBOSE 引數時,VACUUM 會列印有關表的各種統計資訊。這包括有關 relfrozenxidrelminmxid 如何推進以及新凍結頁數的資訊。當自動清理日誌記錄(由 log_autovacuum_min_duration 控制)報告由自動清理執行的 VACUUM 操作時,相同的詳細資訊會出現在伺服器日誌中。

雖然 VACUUM 主要掃描自上次清理以來已修改的頁面,但它也可能急切地掃描一些所有頁面都可見但並非全部凍結的頁面,試圖凍結它們,但 relfrozenxid 僅在掃描完表中可能包含未凍結 XID 的每個頁面後才會被推進。當 relfrozenxidvacuum_freeze_table_age 事務舊時,當使用 VACUUMFREEZE 選項時,或者當所有尚未全部凍結的頁面碰巧需要清理以移除死行版本時,就會發生這種情況。當 VACUUM 掃描表中所有尚未全部凍結的頁面時,它應該將 age(relfrozenxid) 設定為一個略大於 vacuum_freeze_min_age 設定的值(更多的是自 VACUUM 開始以來啟動的事務數)。VACUUM 會將 relfrozenxid 設定為表中剩餘的最舊 XID,因此最終值可能比嚴格要求的要新得多。如果在表中未發出 relfrozenxid 推進的 VACUUM 直到達到 autovacuum_freeze_max_age,那麼很快就會強制對該表進行 autovacuum。

如果由於某種原因 autovacuum 未能清除表中的舊 XID,當資料庫的最舊 XID 距離迴繞點還有 4000 萬事務時,系統將開始發出類似以下的警告訊息:

WARNING:  database "mydb" must be vacuumed within 39985967 transactions
HINT:  To avoid XID assignment failures, execute a database-wide VACUUM in that database.

(如提示所示,手動 VACUUM 應能解決問題;但請注意,VACUUM 應由超級使用者執行,否則它將無法處理系統目錄,這將阻止它能夠推進資料庫的 datfrozenxid。) 如果忽略這些警告,一旦距離迴繞只剩下三百萬事務,系統將拒絕分配新的 XID。

ERROR:  database is not accepting commands that assign new XIDs to avoid wraparound data loss in database "mydb"
HINT:  Execute a database-wide VACUUM in that database.

在此條件下,所有正在進行的事務都可以繼續,但只能啟動只讀事務。修改資料庫記錄或截斷關係的操作將失敗。VACUUM 命令仍然可以正常執行。請注意,與早期版本有時推薦的相反,在這種情況下,停止 postmaster 或進入單使用者模式以恢復正常操作是不必要的,也是不推薦的。相反,請按照以下步驟操作:

  1. 解決舊的已準備事務。您可以透過檢查 pg_prepared_xacts 來查詢 age(transactionid) 較大的行。這些事務應被提交或回滾。
  2. 結束長時間執行的開啟事務。您可以透過檢查 pg_stat_activityage(backend_xid)age(backend_xmin) 較大的行來查詢它們。這些事務應被提交或回滾,或者會話可以使用 pg_terminate_backend 終止。
  3. 刪除任何舊的複製槽。使用 pg_stat_replication 來查詢 age(xmin)age(catalog_xmin) 較大的槽。在許多情況下,這些槽是為不再存在的伺服器或已長時間關閉的伺服器的複製而建立的。如果您為仍然存在且可能仍嘗試連線到該槽的伺服器刪除一個槽,那麼該副本可能需要重建。
  4. 在目標資料庫中執行 VACUUM。資料庫範圍的 VACUUM 是最簡單的;為了減少所需時間,也可以對 relminxid 最舊的表發出手動 VACUUM 命令。在此場景下不要使用 VACUUM FULL,因為它需要一個 XID,因此會失敗,除非在超級使用者模式下,它將消耗一個 XID,從而增加事務 ID 迴繞的風險。也不要使用 VACUUM FREEZE,因為它將執行比恢復正常執行所需的最小工作量更多的工作。
  5. 一旦恢復正常操作,請確保在目標資料庫中正確配置 autovacuum 以避免將來出現問題。

注意

在早期版本中,有時需要停止 postmaster 並以單使用者模式 VACUUM 資料庫。在典型場景中,這不再是必需的,並且應儘可能避免,因為它涉及到系統停機。它也更危險,因為它會停用旨在防止資料丟失的事務 ID 迴繞保護。在此場景下使用單使用者模式的唯一原因是,如果您希望 TRUNCATEDROP 不需要表以避免需要 VACUUM 它們。三百萬事務的安全裕度是為了讓管理員完成這項工作。有關使用單使用者模式的詳細資訊,請參閱 postgres 參考頁。

24.1.5.1. 多事務和迴繞 #

多事務 ID 用於支援多個事務的行鎖定。由於元組頭中用於儲存鎖定資訊的空間有限,當有多個事務同時鎖定一個行時,該資訊將被編碼為一個“多事務 ID”,簡稱 multixact ID。關於哪些事務 ID 包含在任何特定 multixact ID 中的資訊儲存在 pg_multixact 子目錄中,並且只有 multixact ID 出現在元組頭中的 xmax 欄位中。與事務 ID 一樣,multixact ID 實現為 32 位計數器和相應的儲存,所有這些都需要仔細的年齡管理、儲存清理和迴繞處理。有一個單獨的儲存區域,其中包含每個 multixact 的成員列表,它也使用 32 位計數器,並且也必須進行管理。系統函式 pg_get_multixact_members()(在表 9.84中描述)可用於檢查與 multixact ID 關聯的事務 ID。

每當 VACUUM 掃描表的一部分時,它都會將遇到的任何舊於 vacuum_multixact_freeze_min_age 的 multixact ID 替換為另一個值,該值可以是零值、單個事務 ID 或較新的 multixact ID。對於每個表,pg_class.relminmxid 儲存該表中出現的任何元組中的最舊可能 multixact ID。如果此值比 vacuum_multixact_freeze_table_age 舊,則會強制執行強制清理。如上一節所述,強制清理意味著將跳過僅那些已知為全部凍結的頁面。mxid_age() 可用於 pg_class.relminmxid 來查詢其年齡。

強制 VACUUM,無論是什麼原因導致的,都保證能夠推進表的 relminmxid。最終,隨著所有資料庫中的所有表都被掃描並推進其最舊的 multixact 值,可以移除舊 multixacts 的磁碟儲存。

作為安全措施,對於 multixact 年齡大於 autovacuum_multixact_freeze_max_age 的任何表,都會發生強制清理掃描。此外,如果 multixact 成員佔用的儲存空間超過約 10GB,則會對所有表更頻繁地執行強制清理掃描,從 multixact 年齡最老的表開始。這兩種強制掃描都會發生,即使 autovacuum 名義上被停用。成員儲存區最多可以增長到約 20GB,然後達到迴繞點。

與 XID 情況類似,如果 autovacuum 未能清除表中的舊 MXID,當資料庫的最舊 MXID 距離迴繞點還有 4000 萬事務時,系統將開始發出警告訊息。而且,就像在 XID 情況一樣,如果忽略這些警告,一旦距離迴繞只剩下三百萬個,系統將拒絕生成新的 MXID。

當 MXID 用盡時,正常的執行恢復方式與 XID 用盡時非常相似。遵循上一節中的相同步驟,但有以下區別:

  1. 如果執行中的事務和已準備的事務沒有可能出現在 multixact 中的機會,則可以忽略它們。
  2. MXID 資訊未直接顯示在系統檢視(如 pg_stat_activity)中;但是,查詢舊 XID 仍然是確定哪些事務導致 MXID 迴繞問題的好方法。
  3. XID 用盡會阻止所有寫事務,但 MXID 用盡只會阻止一部分寫事務,特別是那些涉及需要 MXID 的行鎖的事務。

24.1.6. 自動清理守護程序 #

PostgreSQL 有一個可選但強烈推薦的功能,稱為自動清理,其目的是自動化 VACUUMANALYZE 命令的執行。啟用後,自動清理會檢查已插入、更新或刪除大量元組的表。這些檢查使用統計資訊收集設施;因此,除非 track_counts 設定為 true,否則無法使用自動清理。在預設配置中,自動清理是啟用的,並且相關的配置引數設定正確。

自動清理守護程序”實際上由多個程序組成。有一個持久的守護程序,稱為自動清理啟動器,它負責為所有資料庫啟動自動清理工作程序。啟動器會將工作分配到一段時間內,並嘗試每 autovacuum_naptime 秒在每個資料庫中啟動一個工作程序。(因此,如果安裝中有N個數據庫,則每 autovacuum_naptime/N 秒將啟動一個新的工作程序。)最多允許 autovacuum_max_workers 個工作程序同時執行。如果需要處理的資料庫多於 autovacuum_max_workers 個,則下一個資料庫將在第一個工作程序完成後立即處理。每個工作程序將檢查其資料庫中的每個表,並在需要時執行 VACUUM 和/或 ANALYZElog_autovacuum_min_duration 可以設定為監視自動清理工作程序的活動。

如果多個大表在短時間內都符合清理條件,則所有自動清理工作程序可能都會長時間忙於清理這些表。這將導致其他表和資料庫在工作程序可用之前無法被清理。在一個數據庫中執行的工作程序數量沒有限制,但工作程序會盡量避免重複其他工作程序已完成的工作。請注意,執行的工作程序數量不計入 max_connectionssuperuser_reserved_connections 的限制。

relfrozenxid 值比 autovacuum_freeze_max_age 事務舊的表總是會被清理(這也適用於那些透過儲存引數修改了 freeze max age 的表;見下文)。否則,如果自上次 VACUUM 以來廢棄的元組數量超過了“清理閾值”,則該表將被清理。清理閾值定義為:

vacuum threshold = Minimum(vacuum max threshold, vacuum base threshold + vacuum scale factor * number of tuples)

其中清理最大閾值是 autovacuum_vacuum_max_threshold,清理基本閾值是 autovacuum_vacuum_threshold,清理比例因子是 autovacuum_vacuum_scale_factor,元組數是 pg_class.reltuples

如果自上次清理以來插入的元組數量超過了定義的插入閾值,則也會清理該表,該閾值定義為:

vacuum insert threshold = vacuum base insert threshold + vacuum insert scale factor * number of tuples

其中清理插入基本閾值是 autovacuum_vacuum_insert_threshold,清理插入比例因子是 autovacuum_vacuum_insert_scale_factor。此類清理可以允許表的部分被標記為所有可見,並允許元組被凍結,這可以減少後續清理所需的工作。對於接收 INSERT 操作但幾乎不接收 UPDATE/DELETE 操作的表,降低表的 autovacuum_freeze_min_age 可能會有益,因為這可以允許元組被更早的清理凍結。廢棄元組的數量和插入元組的數量從累積統計系統獲取;這是一個最終一致的計數,由每個 UPDATEDELETEINSERT 操作更新。如果表的 relfrozenxid 值比 vacuum_freeze_table_age 事務舊,則會執行強制清理以凍結舊元組並推進 relfrozenxid

對於分析,使用類似的條件:閾值,定義為:

analyze threshold = analyze base threshold + analyze scale factor * number of tuples

與自上次 ANALYZE 以來插入、更新或刪除的總元組數進行比較。

分割槽表不直接儲存元組,因此不被自動清理處理。(自動清理會像其他表一樣處理表分割槽。)不幸的是,這意味著自動清理不會對分割槽表執行 ANALYZE,這可能導致引用分割槽表統計資訊的查詢計劃不佳。您可以透過在分割槽表首次填充時手動執行 ANALYZE,並在其分割槽中的資料分佈發生顯著變化時再次執行來解決此問題。

臨時表無法被自動清理訪問。因此,應透過會話 SQL 命令執行適當的清理和分析操作。

預設閾值和比例因子取自 postgresql.conf,但可以為每個表覆蓋它們(以及許多其他自動清理控制引數);有關更多資訊,請參閱儲存引數。如果某個設定已透過表的儲存引數更改,則處理該表時將使用該值;否則將使用全域性設定。有關全域性設定的更多詳細資訊,請參閱第 19.10.1 節

當多個工作程序執行時,自動清理成本延遲引數(請參閱第 19.10.2 節)會在所有執行的工作程序之間進行“平衡”,以便系統的總 I/O 影響與實際執行的工作程序數量無關。但是,處理具有已設定的每個表 autovacuum_vacuum_cost_delayautovacuum_vacuum_cost_limit 儲存引數的表的任何工作程序都不會在平衡演算法中被考慮。

自動清理工作程序通常不會阻止其他命令。如果一個程序嘗試獲取與自動清理持有的 SHARE UPDATE EXCLUSIVE 鎖衝突的鎖,鎖獲取將中斷自動清理。有關衝突的鎖模式,請參閱表 13.2。但是,如果自動清理是為了防止事務 ID 迴繞(即 pg_stat_activity 檢視中的自動清理查詢名稱以 (to prevent wraparound) 結尾),則自動清理不會自動中斷。

警告

定期執行與 SHARE UPDATE EXCLUSIVE 鎖衝突的命令(例如 ANALYZE)可能會有效地阻止自動清理完成。

提交更正

如果您在文件中看到任何不正確之處、與您對特定功能的體驗不符之處或需要進一步澄清之處,請使用此表格報告文件問題。