恢復或回收使用因更新或刪除資料列所佔用的磁碟空間。
更新 PostgreSQL 查詢計劃器使用的資料統計資訊。
防止由於事務 ID 重覆或 multixact ID 重覆而失去非常舊的資料。
這些原因中的每一個都會要求執行不同頻率和範圍的 VACUUM 操作,如以下小節所述。
VACUUM 有兩種執行方式:標準 VACUUM 和 VACUUM FULL。VACUUM FULL 可以回收更多磁碟空間,但執行速度要慢得多。而且,VACUUM 的標準形式可以與線上資料庫同時運作。(SELECT、INSERT、UPDATE 和 DELETE 等指令將繼續正常工作,但在 VACUUM FULL 時,您將無法使用諸如 ALTER TABLE 之類的指令修改資料表的定義。)VACUUM FULL 需要獨占鎖定它正在處理的資料表,因此無法與其他資料表的使用同時進行。因此,一般來說,管理員應該努力使用一般的 VACUUM 而避免進行 VACUUM FULL。
VACUUM 的標準作法是移除資料表和索引中過時的資料列版本,並標記可供將來重複使用的空間。 但是,除非資料表末端的一個或多個頁面變為完全空閒並且可以輕鬆獲取排他資料表鎖定的特殊情況,否則它不會將空間還給作業系統。相比之下,VACUUM FULL 透過寫入完整新版本使其沒有空閒的空間來主動壓縮資料表。這最大限度地減少了資料表的大小,但可能需要很長時間。 它還需要用於資料表新副本的額外磁碟空間,直到操作完成。
常態的資料庫清理通常目標是經常足夠地執行標準 VACUUM 以避免需要 VACUUM FULL。autovacuum 背景程序嘗試以這種方式工作,實際上永遠不會發出 VACUUM FULL。在這種方法中,這個想法並不是將資料表保持在最小尺寸,而是為了保持磁碟空間的穩定狀態使用:每個資料表都佔用相當於其最小尺寸的空間,再加上在 VACUUM 之間使用的空間很大,儘管可以使用 VACUUM FULL 將表縮回到最小大小並將磁碟空間還回到作業系統,但如果資料表將來會再次增長,則沒有多大意義。 因此,適度頻繁的標準 VACUUM 運行比用於維護大量更新資料表的罕見 VACUUM FULL 運行更好。
有些管理者更喜歡自己安排資料庫清理作業,例如在負載較低時在夜間進行所有工作。按照固定的時間表進行資料庫清理作業的困難在於,如果資料表在更新活動中出現意外的峰值,則可能會變得臃腫到 VACUUM FULL 真的需要回收空間。使用自動清理背景程序緩解了這個問題,因為背景程序會根據更新活動動態調度清理作業。除非您有一個非常可預測的工作量,否則完全停用該背景程序是不明智的。一個可能的折衷辦法是設定背景程序的參數,以便它僅對異常繁重的更新活動作出反應,從而避免事情失控,而預定的 VACUUM 參數是能在典型的情況下完成大部分工作。
autovacuum 背景程序(如果啟用的話)會在資料表內容發生相當的變化時自動發出 ANALYZE 指令。但是,管理員可能更喜歡依靠手動調度的 ANALYZE 操作,尤其是如果知道資料表上的更新活動不會影響「有興趣的」欄位的統計信息。背景程序嚴格按照插入或更新的資料列數的安排 ANALYZE;不過它並不知道這是否會導致有意義的統計變化。
與資料清理恢復空間一樣,頻繁更新統計數據對於大量更新的資料表比對很少更新的資料表更有用。但即使對於大量更新的資料表,如果資料的統計分佈變化不大,也可能不需要進行統計更新。一個簡單的經驗法則是考慮資料表中欄位的最小值和最大值的變化。例如,包含行更新時間的 timestamp 欄在插入和更新資料列時會不斷增加最大值;這樣的欄位可能需要更頻繁的統計更新,而不是包含網頁內容的網址欄位。URL 欄位可能會經常收到更新,但其內容的統計分佈可能變化比較慢。
可以在特定的資料表上執行 ANALYZE,甚至可以在資料表中特定的欄位上執行ANALYZE,因此如果應用程序需要,可以更靈活地更新某些統計資訊。然而,在實務上,通常最好僅分析整個資料庫,因為這是一種快速操作。ANALYZE 以資料表中資料列的隨機抽樣而不是讀取每一個資料列。
此外,預設情況下,有關 SELECT 函數的訊息有限。但是,如果建立使用函數呼叫的表示式索引,則會收集有關該函數的有用統計訊息,這可以極大地改進使用表示式索引的查詢計劃。
定期清理能解決問題的原因是 VACUUM 會將資料列標記為凍結,表明它們是由過去的事務插入的,以至於插入事務的影響肯定對所有目前和未來的事務都可見。使用 modulo-232 運算比較普通 XID。這意味著對於每個普通的 XID,有20億個「較舊」的 XID 和 20 個「較新」的 XID;另一種說法是普通的 XID 空間是圓形的,沒有端點。因此,一旦使用特定的普通 XID 建立了資料列版本,無論我們在談論哪種正常的 XID,資料列版本對於接下來的 20 億次交易看起來都是“過去的”。如果資料列版本在超過 20 億次交易後仍然存在,那麼它將來會突然出現。為了防止這種情況,PostgreSQL 保留了一個特殊的 XID,FrozenTransactionId,它不遵循正常的 XID 比較規則,並且總是被認為比每個普通的 XID 都舊。凍結資料列版本被視為插入 XID 是 FrozenTransactionId,因此它們對於所有正常事務而言似乎都是「過去」而不管繞回重覆的問題,因此這些資料列版本在刪除之前有效,無論多長時間都是。
此外,系統目錄可能包含 xmin 等於 BootstrapTransactionId(1) 的資料列,表示它們是在 initdb 的第一階段插入的。與 FrozenTransactionId 一樣,此特殊 XID 被視為比每個普通 XID 更舊。
這意味著如果資料表沒有以其他方式進行清理,則每次 autovacuum_freeze_max_age 減去 vacuum_freeze_min_age 的事務數量時,將在其上執行 autovacuum。對於經常用於空間回收目的而被清理的資料表,這一點並不重要。但是,對於靜態資料表(包括接收插入但沒有更新或刪除的資料表),不需要清理進行空間回收,因此嘗試最大化非常大的靜態資料表上強制自動清理之間的間隔會很有用。顯然,可以透過增加 autovacuum_freeze_max_age 或減少 vacuum_freeze_min_age 來達到此目的。
vacuum_freeze_table_age 的有效最大值為 0.95 * autovacuum_freeze_max_age;高於此值的設定將被限制為最大值。高於 autovacuum_freeze_max_age 的值是沒有意義的,因為無論如何都會在該點觸發n防止交易重疊的自動清理,並且 0.95 乘數在此之前留下一些喘息空間來執行手動 VACUUM。根據經驗,vacuum_freeze_table_age 應設定為略低於 autovacuum_freeze_max_age 的值,留下足夠的間隙,以便在該間隙中執行由日常刪除和更新活動觸發定期的 VACUUM 或 autovacuum。將它設定得太近可能會導致防止交易重疊的自動清理,即使該資料表最近被清理以回收空間,而較低的值還是會導致更頻繁的積極清理。
增加 autovacuum_freeze_max_age(以及 vacuum_freeze_table_age)的唯一缺點是資料庫叢集的 pg_xact 和 pg_commit_ts 子目錄將佔用更多空間,因為它必須儲存提交狀態和(如果啟用了 track_commit_timestamp)所有事務的時間戳記回到 autovacuum_freeze_max_age horizon。提交狀態每個交易事務使用兩個位元,因此如果 autovacuum_freeze_max_age 設定為其最大允許值 20 億,則 pg_xact 可以增長到大約 0.5 GB,pg_commit_ts 可以增長到大約 20 GB,這與總資料庫大小相比這是微不足道的。建議將 autovacuum_freeze_max_age 設定為其最大允許值。否則,根據您願意允許 pg_xact 和 pg_commit_ts 儲存的內容進行設定。(一般情況下,2 億次交易,轉換為大約 50 MB 的 pg_xact 儲存空間和大約 2 GB 的pg_commit_ts 儲存空間。)
減少 vacuum_freeze_min_age 的一個缺點是它可能導致 VACUUM 進行無謂的工作:如果此後很快更新資料列(導致它獲取新的 XID),凍結資料列版本會浪費時間。因此,設定應該足夠大,以至於資料列不會被凍結,直到它們不再可能更新為止。
為了追踪資料庫中最早解凍的 XID 的值,VACUUM 將 XID 統計訊息儲存在系統資料表 pg_class 和 pg_database 中。特別是,資料表 pg_class 的 relfrozenxid 欄位包含該資料表的最後一個積極 VACUUM 使用的凍結截止 XID。由 XID 早於此截止 XID 的事務插入,則所有資料列都保證已被凍結。同理,資料庫的 pg_database 的 datfrozenxid 欄位是該資料庫中出現的未凍結 XID 的下限 - 它只是資料庫中每個資料表 relfrozenxid 的最小值。檢查此訊息的便捷方法是執行以下查詢:
age 欄位測量從截止 XID 到目前事務的 XID 的事務數。
VACUUM 通常僅掃描自上次清理以來已修改的頁面,但只有在掃描可能包含未凍結 XID 資料表的每個頁面時才能提升 relfrozenxid。當 relfrozenxid 超過 vacuum_freeze_table_agetransactions 時,或當使用 VACUUM 的 FREEZE 選項時,又或當所有尚未全部凍結的頁面碰巧需要清理以刪除過期資料列版本時,才會發生這種情況。當 VACUUM 掃描資料表中尚未全部凍結的每個頁面時,應將 age(relfrozenxid)設定為比 vacuum_freeze_min_age 設定略多一點的值(更多是自 VACUUM 啟動以來啟動的事務數量)。如果在達到 autovacuum_freeze_max_age 之前沒有在資料表上發出 relfrozenxid-advance 的 VACUUM,則很快將強制執行該資料表的 autovacuum。
如果由於某種原因 autovacuum 無法從資料表中清除舊的 XID,當資料庫最舊的 XID 從重疊點到達一千萬個事務時,系統將開始發出這樣的警告消息:
(應該按照提示的建議進行手動 VACUUM 解決問題;但請注意,VACUUM 必須由超級使用者執行,否則它將無法處理系統目錄,就無法推進資料庫的 datfrozenxid。)這些警告如果被忽略,系統將關閉並拒絕啟動任何新的事務,一旦剩下的事務 XID 在重疊前少於 100 萬:
Multixact ID 用於支援多個事務的資料列鎖定。由於 tuple 標頭中只有有限的空間來儲存鎖定訊息,因此只要有多個事務同時鎖定一個資料列,該訊息就會被編碼為“multiple transaction ID”或簡稱 Multixact ID。 有關哪些事務 ID 包含在任何特定 multixact ID 中的訊息將單獨儲存在 pg_multixact 目錄中,並且只有 multixact ID 出現在 tuple 標頭中的 xmax 字串中。與事務 ID 一樣,multixact ID 實作為 32 位元計數器和相對應的儲存,所有這些都需要仔細的存續管理,儲存清理和環繞處理。有一個單獨的儲存區域,用於保存每個 multixact 中的成員列表,該列表也使用 32 位元計數器,必須進行管理。
無論是什麼原因導致積極的 VACUUM 掃描都能夠提升該資料表的值。最終,由於掃描了所有資料庫中的所有資料表並提升了其最舊的 multixact 值,因此可以移除舊的 multixacts 的磁碟儲存。
其 relfrozenxid 值大於 autovacuum_freeze_max_age 事務舊的資料表總是被清理(這也適用於那些已通過儲存參數修改了凍結最大年齡的資料表;請參閱下文)。 否則,如果自上一個 VACUUM 以來廢棄的 tuple 數超過“清理閾值(vacuum threshold)”,則對該資料表進行清理。 清理閾值的定義為:
對於分析,使用類似的條件:此閾值定義為:
與自上次 ANALYZE 以來插入、更新或刪除的 tuple 總數進行比較。
autovacuum 無法存取臨時資料表。因此,應透過直接執行 SQL 指令進行適當的清理和分析操作。