PostgresSQL-平常清理

22.1. 平常清理

因爲如下幾個緣由,必須週期性運行 PostgreSQL 的 VACUUM 命令∶php

  1. 恢復那些由已更新的或已刪除的行佔據的磁盤空間。html

  2. 更新 PostgreSQL 查詢規劃器使用的數據統計信息。算法

  3. 避免由於事務 ID 重疊形成的老舊數據的丟失。sql

對上面每一個條件進行 VACUUM 操做的頻率和範圍因不一樣的節點而不一樣。 所以,數據庫管理員必須理解這些問題而且開發出合適的維護策略。 本節的重點就放在解釋這些高級別的問題; 至於命令語法的細節,請參閱 VACUUM 命令手冊頁。數據庫

從 PostgreSQL 7.2 開始, VACUUM 的標準形式能夠和普通的數據庫操做 (selects, inserts, updates, deletes, 但不包括表定義的修改)。 所以平常的清理也再也不象之前的版本那樣具備干擾性, 也再也不那麼特別要求安排在天天的低使用的時間裏進行。後端

從 PostgreSQL 8.0 開始,有一些配置參數能夠設置, 用來進一步減少後端清理的的性能影響。參閱 Section 17.4.4安全

在 PostgreSQL 8.1 中增長了一個自動的機制,用於執行必要的 VACUUM 操做。參閱 Section 22.1.4服務器

22.1.1. 恢復磁盤空間

在正常的 PostgreSQL 操做裏, 對一行的UPDATEDELETE並未當即刪除舊版本的數據行。 這個方法對於獲取多版本並行控制的好處是必要的(參閱 Chapter 12): 若是一個行的版本仍有可能被其它事務看到,那麼你就不能刪除它。 但到了最後,不會有任何事務對過時的或者已經刪除的元組感興趣。 而它佔據的空間必須爲那些新的元組使用而回收, 以免對磁盤空間增加的無休止的需求。這件事是經過運行 VACUUM 實現的。併發

很明顯,那些常常更新或者刪除元組的表須要比那些較少更新的表清理的更頻繁一些。 因此,設置一個週期性的 cron 任務 VACUUM 那些選定的表,而忽略那些已經知道變化比較少的表. 這個方法只是在你擁有大量更新頻繁的表和大量不多更新的表的時候有意義 — 清理一個小表的額外開銷根本不值得擔憂.mvc

VACUUM 命令有兩個變種。第一種形式,叫作"懶漢 vacuum"或者只是 VACUUM, 在表和索引中標記過時的數據爲未來使用;它並試圖當即恢復這些過時數據使用的空間。 所以,表文件不會縮小,而且任何文件中沒有使用的空間都不會返回給操做系統。 這個變種的 VACUUM 能夠和一般的數據庫操做併發執行。

第二種形式是 VACUUM FULL 命令。 這個形式使用一種更加激進的算法來恢復過時的的行版本佔據的空間。 任何 VACUUM FULL 釋放的空間都當即返回給操做系統。 可是,這個形式的VACUUM 命令在進行 VACUUM FULL 一個表的時候在其上要求一個排他鎖。 所以,常用 VACUUM FULL 會對併發數據庫查詢有着很是糟糕的影響。

標準形式的 VACUUM 最適合用於維護至關程度的磁盤用量的穩定狀態。 若是你須要把磁盤空間歸還給操做系統,那麼你可使用 VACUUM FULL — 不過若是釋放的磁盤空間又會很快再次被分配又怎樣? 若是維護更新頻繁的表,那麼中等頻率的屢次標準 VACUUM 運行方法比很低頻率的 VACUUM FULL 更好。

對於大多數節點而言,咱們推薦的習慣是在一天中的低使用的時段安排一次整個數據庫的 VACUUM, 必要時外加對更新頻繁的表的更常常的清理。 (有些環境下,對那些更新很是頻繁的表可能會每幾分鐘就 VACUUM 一次。) 若是你的集羣中有多個數據庫,別忘記對每一個庫進行清理; vacuumdb 腳本可能會幫你的忙。

若是你知道本身剛刪除掉一個表中大部分的行,那麼咱們建議使用VACUUM FULL, 這樣該表的穩定態尺寸能夠由於VACUUM FULL更富侵略性的方法而顯著減少。 平常的磁盤空間清理,請使用 VACUUM,而不是 VACUUM FULL

若是你有一個表,它的內容常常被徹底刪除,那麼能夠考慮用 TRUNCATE,而不是後面跟着 VACUUM 的 DELETE。 TRUNCATE 當即刪除整個表的內容, 而不要求隨後的 VACUUM 或者VACUUM FULL 來恢復如今沒有用的磁盤空間。

22.1.2. 更新規劃器統計

PostgreSQL 的查詢規劃器依賴一些有關表內容的統計信息用覺得查詢生成好的規劃。 這些統計是經過ANALYZE 命令得到的,你能夠直接調用這條命令, 也能夠把它當作 VACUUM 裏的一個可選步驟來調用。 擁有合理準確的統計是很是重要的,不然,選擇了惡劣的規劃極可能下降數據庫的性能。

和爲了回收空間作清理同樣,常常更新統計信息也是對更新頻繁的表更有用。 不過,即便是更新很是頻繁的表,若是它的數據的統計分佈並不常常改變,那麼也不須要更新統計信息。 一條簡單的拇指定律就是想一想表中字段的最大很最小值改變的幅度。 好比,一個包含行更新時間的 timestamp 字段將是隨着行的追加和更新穩定增加最大值的; 這樣的字段可能須要比那些包含訪問網站的 URL 的字段更頻繁一些更新統計信息。 那些 URL 字段可能改變得同樣頻繁,可是其數值的統計分佈的改變相對要緩慢得多。

咱們能夠在特定的表,甚至是表中特定的字段上運行 ANALYZE, 因此若是你的應用有需求的話,咱們是能夠對某些信息更新得比其它信息更頻繁的。 不過,在實際中,這種作法的有用性是值得懷疑的。 從 PostgreSQL 7.2 開始, ANALYZE 是一項至關快的操做,即時在大表上也很快, 由於它使用了統計學上的隨機採樣的方法進行行採樣, 而不是把每一行都讀取進來。所以,每隔一段時間對整個數據庫運行一便這條命令可能更簡單。

提示: 儘管用 ANALYZE 按字段進行挖掘的方式可能不是很實用, 但你可能仍是會發現值得按字段對 ANALYZE 收集的統計信息的詳細級別進行調整。 那些常常在WHERE子句裏使用的字段若是有很是不規則的數據分佈, 那麼就可能須要比其它字段更細緻的數據圖表.參閱 ALTER TABLE SET STATISTICS.

咱們對大多數節點都建議在天天的低使用時段安排一次數據庫範圍的 ANALYZE: 這個任務能夠有效地和天天的 VACUUM 組合在一塊兒。 不過,這對那些表統計信息改變相對緩慢的節點可能會過於誇張, 並且少一些的 ANALYZE 也足夠了。

22.1.3. 避免事務 ID 重疊形成的問題

PostgreSQL 的 MVCC 事務語意依賴於比較事務 ID(XID)的數值: 一條帶有大於當前事務的 XID 的插入 XID 的行版本是"屬於將來的", 而且不該爲當前事務可見。可是由於事務 ID 的大小有限(在咱們寫這些的時候是 32 位), 若是一次集羣若是運行的時間很長(大於 4 十億次事務), 那麼它就要受到事務 ID 重疊的折磨:XID 計數器回到零位, 而後忽然間全部之前的事務就變成看上去是在未來的 — 這意味着它們的輸出將變得可見。 簡而言之,可怕的數據丟失,(實際上數據仍然在那裏,可是若是你沒法獲取數據,這麼說也只是幸災樂禍。)

在 PostgreSQL 7.2 以前, 防護 XID 重疊的惟一辦法就是至少每4十億事務就從新作一次initdb。 這種作法對高流量的節點而言固然不是使人滿意的作法,因此咱們設計了更好的方法。 新的方法容許某個服務器仍然保持運行狀態,不須要 initdb 或者任何類型的重啓。 代價就是下面這樣的維護要求: 數據庫中的每一個表都必須在每十億次事務中至少清理一次 

從實際角度出發,這個要求不算一個很繁重的要求, 可是由於若是咱們沒能知足這個要求的後果是所有數據的丟失(而不只僅是磁盤空間的浪費或者性能的降低), 咱們製做了一些特殊的東西來幫助數據庫管理員避免災難的發生。 對於集羣中的每一個數據庫,PostgreSQL 都跟蹤自上次全數據庫範圍 VACUUM 以來的時間。 若是任何數據庫接近了十億次事務的危險級別,系統就開始發出警告信息。 若是什麼都不幹,那麼系統最終會中止正常的操做,直到進行了合適的手工操做。 本節剩下的部分給出這方面的細節。

XID 比較的新方法剝離出兩個特殊的 XID,數字 1 和 2 (BootstrapXID 和 FrozenXID)。 這兩個 XID 老是被認爲表任何普通的 XID 舊。普通的 XID(那些大於 2 的)使用模-231運算進行比較。 這就意味着對於每一個普通的 XID,老是有二十億個 XID 是"更舊"以及二十億個 XID"更新"; 表達這個意思的另一個方法是普通的 XID 空間是沒有終點的環。 所以,一旦一條元組帶着特定的普通 XID 建立出來,那麼該元組 將在之後的二十億次事務中表現得是"在過去",而無論咱們說的是哪一個普通 XID。 若是該元組在超過二十億次事務以後仍然存在, 那麼它就會忽然變成在未來的元組。爲了不數據丟失, 老的元組必須在到達二十億次事務的年齡以前的某個時候賦予 XID FrozenXID。 一旦它被賦予了這個特殊的 XID,那麼它們在全部普通事務面前表現爲 "在過去",而無論事務 ID 是否重疊, 所以這樣的元組直到刪除以前都會無缺,無論要保存多長時間.這個 XID 的從新賦值是VACUUM 控制的.

VACUUM 的正常策略是給任何其普通 XID 有超過十億次已過去事務行版本從新賦值爲 FrozenXID。 這個策略保留了原來的插入 XID 直到該數值再也不使人感興趣爲止。 (實際上,大多數行版本將可能在尚未"凍結"以前就完成生存和消亡了)。 在這個策略下,任何表在兩次 VACUUM 運行之間的最大的安全間隔是十億次事務: 若是你等的時間更長,那麼最後就可能就會有一條不夠老的行版本在從新賦值時變成比二十億次事務更老, 並所以重疊到了將來 — 也就是說,你失去它了。(固然,它在另外二十億次事務以後會從新出現,不過那樣也無濟於事。)

由於上面的緣由,咱們須要週期性地運行 VACUUM, 因此很難有哪一個表會到十億次事務尚未清理過。可是,爲了幫助管理員確保知足了這個要求, VACUUM 在系統表pg_database 裏存儲了事務 ID 統計。 尤爲是一個數據庫的 pg_database 行中的 datfrozenxid 字段在任何數據庫範圍的 VACUUM 操做(也就是沒有聲明任何表的VACUUM)以後將會被更新。 這個字段裏存儲的數值是該 VACUUM 命令使用的凍結終止的 XID。 系統保證在該數據庫中全部比這個終止 XID 老的普通 XID 都被 FrozenXID 代替。 檢查這個信息的一個便利的方法是執行下面的查詢

SELECT datname, age(datfrozenxid) FROM pg_database;

age 字段用於測量從停止 XID 到當前事務的 XID 的數目。

使用了這種標準的凍結策略,對一個剛清理過的數據庫而言, age 字段將從十億處開始。當age到達二十億次的時候, 數據庫必須再次清理以免事務標識重疊形成的問題。 咱們建議的策略是至少每半個十億次(5億次)事務 VACUUM 一次數據庫, 這樣就能夠保證足夠的安全邊界範圍.爲了幫助知足這條規則, 若是有任何 pg_database 記錄顯示出超過15億次事務的 age, 那麼每次數據庫範圍的VACUUM 都會自動發出一條警告,好比:

play=# VACUUM;
WARNING:  database "mydb" must be vacuumed within 177009986 transactions
HINT:  To avoid a database shutdown, execute a full-database VACUUM in "mydb".
VACUUM

若是忽略了上面這樣的 VACUUM 信息,若是距離事務 ID 重疊小於 1 千萬次, 那麼 PostgreSQL 就會在每次事務開始前發出相似上面的警告。 若是這些警告仍是被忽略了,那麼系統將在距離重疊小於 1 百萬次的時候關閉,而且拒絕執行任何新的事務:

play=# select 2+2;
ERROR:  database is shut down to avoid wraparound data loss in database "mydb"
HINT:  Stop the postmaster and use a standalone backend to VACUUM in "mydb".

這個 1 百萬的事務安全邊界留下來用於讓管理員在不丟失數據的狀況下進行恢復, 方法是手工執行所須要的 VACUUM 命令。不過,由於一旦進入了安全關閉模式,系統就不能再執行命令, 作這件事情的惟一的方法是中止 postmaster,使用一個單獨運行的後端來執行 VACUUM。 關閉模式不會強制於獨立運行的後端。參閱 postgres 手冊也獲取有關使用獨立運行後端的細節。

帶着 FREEZE 選項的 VACUUM 使用了更大膽的凍結策略: 若是行版本已經老得被全部打開的事務看作是良好的, 那麼就都凍結.特別是若是在一個空閒的數據庫上運行 VACUUM FREEZE,那麼就保證該數據庫中全部的行版本都被凍結。 所以,只要該數據庫沒有其它的變化,那麼它就不須要後續的清理以免事務 ID 重疊問題。 這個技巧被 initdb 用於準備template0數據庫。 咱們也應該用這個方法對全部在 pg_database表裏標記着 datallowconn = false的數據庫進行初始化, 由於咱們尚未任何便利的方法 VACUUM 一個你沒法聯接的數據庫。

22.1.4. auto-vacuum 守護進程

從 PostgreSQL 8.1 開始,系統帶有一個額外的可選服務進程, 叫作 autovacuum 守護進程,它的目的是自動執行 VACUUM 和 ANALYZE 命令。在打開這個選項以後,autovacuum 守護進程將週期性運行而且檢查那些有大量插入,更新或者刪除元組操做的表。 這些檢查使用行級別的統計收集設施;所以,除非把 stats_row_level 和 stats_row_level 設置爲 true,不然沒法使用 autovacuum 守護。 還有,在爲 superuser_reserved_connections 選擇數值的時候,不要忘記給 autovacuum 進程保留一個槽位。

若是打開了 autovacuum 守護,那麼它會每隔 autovacuum_naptime 秒鐘運行一次,而且檢查應該處理哪一個數據庫。 任何臨近事務 ID 重疊的數據庫都會被當即處理。這個時候,autovacuum 發出一個數據庫反胃的 VACUUM 調用,若是是模板數據庫,則發出 VACUUM FREEZE, 而後終止。若是沒有數據庫複合這個標準,則選擇被上次 autovacuum 處理時間最遠的那個數據庫。 這種狀況下,該數據庫裏的表被檢查,而後根據須要發出獨立的 VACUUM 或者 ANALYZE 命令。

對於每一個表,用兩個條件來判斷應該使用哪一個操做。 若是上次 VACUUM 以後的過時元組的數量超過了"清理閾值(vacuum threshold)", 那麼就清理改表。清理閾值是定義爲:

清理閾值 = 清理基本閾值 + 清理縮放係數 * 元組數
(vacuum threshold = vacuum base threshold + vacuum scale factor * number of tuples)

這裏的清理基本閾值是 autovacuum_vacuum_threshold, 清理的縮放係數是 autovacuum_vacuum_scale_factor, 元組的數目是 失效的元組數目是從統計收集器裏面獲取的;這事一個半精確的計數,由每次 UPDATE 和 DELETE 操做更新。 (它只是半精確的是由於在重載下,有些信息可能會丟失。) 爲了分析,使用了一個相似的條件:分析閾值,定義爲

分析閾值 = 分析基本閾值 + 分析縮放係數 * 元組數目
(analyze threshold = analyze base threshold + analyze scale factor * number of tuples)

它會和上次 ANALYZE 插入,更新,或者刪除的元組總數進行比較。

缺省的閾值和伸縮係數是從 postgresql.conf 裏面取得的, 不過,咱們能夠以每一個表獨立設置的方式覆蓋它,方法就是在系統表 pg_autovacuum 裏輸入記錄。 若是 pg_autovacuum 裏面存在對某個特定表的行,那麼就使用它聲明的設置; 不然使用全局設置。參閱 Section 17.9 獲取有關全局設置的更多細節。

除了基本閾值和縮放係數以外,在 pg_autovacuum 裏還有三個參數能夠爲每一個表進行設置。 首先,pg_autovacuum.enabled 能夠設置爲 false, 讓 autovacuum 守護進程徹底忽略某個表。這種狀況下,autovacuum 只有在爲了不事務 ID 重疊清理整個數據庫的時候纔會動那個表。另外兩個參數,清理開銷延遲 (pg_autovacuum.vac_cost_delay)和清理開銷限制 (pg_autovacuum.vac_cost_limit), 用於爲 基於開銷的清理延遲 特性設置表相關的數值。

若是在 pg_autovacuum 裏任何數值設置爲負數, 或者在 pg_autovacuum 裏就根本沒有出現特定表的數據行, 那麼使用 postgresql.conf 裏面對應的數值。

目前沒有任何製做 pg_autovacuum 記錄的支持, 只能手工向該系統表中 INSERT。這個特性將在之後的版本中改進, 而且這個系統表的定義也頗有可能會改變。

相關文章
相關標籤/搜索