amcheck
模組提供了一些函式,允許您驗證關係結構在邏輯上是否一致。
B-Tree 檢查函式會驗證特定關係表示結構中的各種不變數。索引掃描和其他重要操作背後的訪問方法函式的正確性依賴於這些不變數始終成立。例如,某些函式會驗證 B-Tree 頁是否以“邏輯”順序包含項(例如,對於基於 text
的 B-Tree 索引,索引元組應按排序的字母順序排列)。如果該特定不變數未能成立,我們可能會期望受影響頁上的二分查詢錯誤地引導索引掃描,從而導致 SQL 查詢的錯誤答案。如果結構看起來有效,則不會引發錯誤。在執行這些檢查函式時,search_path 會被臨時更改為 pg_catalog, pg_temp
。
驗證是使用索引掃描本身所使用的相同過程執行的,這可能包括使用者定義的運算子類程式碼。例如,B-Tree 索引驗證依賴於與一個或多個 B-Tree 支援函式 1 的比較。有關運算子類支援函式的詳細資訊,請參閱 第 36.16.3 節。
與會透過引發錯誤報告損壞的 B-Tree 檢查函式不同,堆檢查函式 verify_heapam
會檢查表並嘗試返回一個行集,每檢測到一個損壞報告一行。儘管如此,如果 verify_heapam
所依賴的機制本身已損壞,該函式可能無法繼續,並可能改為引發錯誤。
可以授予非超級使用者執行 amcheck
函式的許可權,但在授予這些許可權之前,應仔細考慮資料安全和隱私問題。儘管這些函式生成的損壞報告更多地關注損壞資料的結構和發現的損壞型別,而不是關注損壞資料的具體內容,但獲得執行這些函式許可權的攻擊者(尤其是在攻擊者還能引起損壞時)可能能夠從這些訊息中推斷出資料本身的一些資訊。
bt_index_check(index regclass, heapallindexed boolean, checkunique boolean) returns void
bt_index_check
測試其目標 B-Tree 索引是否遵循各種不變數。示例用法
test=# SELECT bt_index_check(index => c.oid, heapallindexed => i.indisunique), c.relname, c.relpages FROM pg_index i JOIN pg_opclass op ON i.indclass[0] = op.oid JOIN pg_am am ON op.opcmethod = am.oid JOIN pg_class c ON i.indexrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE am.amname = 'btree' AND n.nspname = 'pg_catalog' -- Don't check temp tables, which may be from another session: AND c.relpersistence != 't' -- Function may throw an error when this is omitted: AND c.relkind = 'i' AND i.indisready AND i.indisvalid ORDER BY c.relpages DESC LIMIT 10; bt_index_check | relname | relpages ----------------+---------------------------------+---------- | pg_depend_reference_index | 43 | pg_depend_depender_index | 40 | pg_proc_proname_args_nsp_index | 31 | pg_description_o_c_o_index | 21 | pg_attribute_relid_attnam_index | 14 | pg_proc_oid_index | 10 | pg_attribute_relid_attnum_index | 9 | pg_amproc_fam_proc_index | 5 | pg_amop_opr_fam_index | 5 | pg_amop_fam_strat_index | 5 (10 rows)
此示例顯示了一個會話,該會話對資料庫 “test” 中 10 個最大的目錄索引執行驗證。對於那些是唯一索引的子集,請求驗證是否存在堆元組作為索引元組。由於未引發任何錯誤,因此所有測試過的索引似乎在邏輯上是一致的。當然,此查詢可以輕鬆修改為對資料庫中支援驗證的每個索引呼叫 bt_index_check
。
bt_index_check
會在其目標索引及其所屬的堆關係上獲取 AccessShareLock
。此鎖模式與簡單 SELECT
語句在關係上獲取的鎖模式相同。 bt_index_check
不會驗證跨越子/父關係的節點,但在 heapallindexed
為 true
時,會驗證在索引中是否存在所有堆元組作為索引元組。當 checkunique
為 true
時,bt_index_check
會檢查唯一索引中重複條目是否最多隻有一個可見。當需要對即時生產環境中的例行、輕量級損壞測試時,使用 bt_index_check
通常能提供驗證的徹底性和限制應用程式效能和可用性影響之間的最佳權衡。
bt_index_parent_check(index regclass, heapallindexed boolean, rootdescend boolean, checkunique boolean) returns void
bt_index_parent_check
測試其目標 B-Tree 索引是否遵循各種不變數。可選地,當 heapallindexed
引數為 true
時,該函式會驗證索引中應找到的所有堆元組的存在。當 checkunique
為 true
時,bt_index_parent_check
會檢查唯一索引中重複條目是否最多隻有一個可見。當可選的 rootdescend
引數為 true
時,驗證會透過從每個元組的根節點重新搜尋來重新查詢葉節點上的元組。 bt_index_parent_check
可執行的檢查是 bt_index_check
可執行的檢查的超集。 bt_index_parent_check
可以看作是 bt_index_check
的更徹底變體:與 bt_index_check
不同,bt_index_parent_check
還會檢查跨越父/子關係的節點,包括檢查索引結構中是否存在缺失的下行連結。 bt_index_parent_check
遵循一般的約定,即如果發現邏輯不一致或其他問題,則會引發錯誤。
bt_index_parent_check
要求目標索引上的 ShareLock
(也會在堆關係上獲取 ShareLock
)。這些鎖會阻止 INSERT
、UPDATE
和 DELETE
命令進行併發資料修改。這些鎖還會阻止底層關係被 VACUUM
以及所有其他實用程式命令併發處理。請注意,該函式僅在執行時持有鎖,而不是在整個事務中持有。
bt_index_parent_check
的額外驗證更有可能檢測到各種病態情況。這些情況可能涉及被檢查索引使用的錯誤實現的 B-Tree 運算子類,或者假設為底層 B-Tree 索引訪問方法程式碼中未發現的錯誤。請注意,與 bt_index_check
不同,bt_index_parent_check
在啟用熱備用模式(即在只讀物理副本上)時無法使用。
gin_index_check(index regclass) returns void
gin_index_check
測試其目標 GIN 索引是否具有一致的父子元組關係(沒有父元組需要調整元組)並且頁面圖尊重平衡樹不變數(內部頁面僅引用葉頁面或僅內部頁面)。
bt_index_check
和 bt_index_parent_check
都會在 DEBUG1
和 DEBUG2
嚴重級別輸出有關驗證過程的日誌訊息。這些訊息提供了有關驗證過程的詳細資訊,可能對 PostgreSQL 開發人員感興趣。高階使用者也可能會發現此資訊很有用,因為它在驗證實際檢測到不一致時提供了額外的上下文。在互動式 psql 會話中執行
SET client_min_messages = DEBUG1;
在執行驗證查詢之前,可以以可管理的詳細程度顯示有關驗證進度的訊息。
verify_heapam(relation regclass, on_error_stop boolean, check_toast boolean, skip text, startblock bigint, endblock bigint, blkno OUT bigint, offnum OUT integer, attnum OUT integer, msg OUT text) returns setof record
檢查表、序列或物化檢視是否存在結構損壞(頁面中的資料格式無效)和邏輯損壞(頁面結構有效但與資料庫叢集的其餘部分不一致)。
識別以下可選引數
on_error_stop
如果為 true,則損壞檢查將在發現任何損壞的第一個塊的末尾停止。
預設為 false。
check_toast
如果為 true,則會對炸開的值與目標關係的 TOAST 表進行檢查。
此選項已知速度很慢。此外,如果 toast 表或其索引損壞,對其進行炸開值檢查可能會導致伺服器崩潰,儘管在許多情況下這隻會產生錯誤。
預設為 false。
skip
如果不是 none
,則損壞檢查會跳過已指定為 all-visible 或 all-frozen 的塊。有效選項為 all-visible
、all-frozen
和 none
。
預設為 none
。
startblock
如果指定,則損壞檢查從指定的塊開始,跳過所有前面的塊。指定超出目標表塊範圍的 startblock
會導致錯誤。
預設情況下,檢查從第一個塊開始。
endblock
如果指定,則損壞檢查將在指定的塊結束,跳過所有剩餘的塊。指定超出目標表塊範圍的 endblock
會導致錯誤。
預設情況下,會檢查所有塊。
對於檢測到的每個損壞,verify_heapam
返回一個包含以下列的行
blkno
包含損壞頁面的塊的編號。
offnum
損壞元組的偏移量編號。
attnum
如果損壞特定於列而不是整個元組,則為元組中損壞列的屬性編號。
msg
描述檢測到的問題的訊息。
heapallindexed
驗證 #當 B-Tree 驗證函式的 heapallindexed
引數為 true
時,將針對目標索引關係關聯的表執行額外的驗證階段。這包括一個“虛擬” CREATE INDEX
操作,該操作會針對一個臨時的、記憶體中的彙總結構(在基本驗證的第一階段需要時構建)檢查所有假設的新索引元組是否存在。彙總結構會“指紋化”目標索引中找到的每個元組。 heapallindexed
驗證的高層原理是,一個等同於現有目標索引的新索引必須只包含可以在現有結構中找到的條目。
額外的 heapallindexed
階段會增加顯著的開銷:驗證通常需要幾倍的時間。但是,在執行 heapallindexed
驗證時,關係級鎖沒有變化。
彙總結構的大小受 maintenance_work_mem
限制。為了確保每個應在索引中表示的堆元組,不檢測到不一致的機率超過 2%,每個元組大約需要 2 位元組記憶體。隨著每個元組可用記憶體的減少,遺漏不一致的機率會緩慢增加。這種方法顯著限制了驗證的開銷,同時僅略微降低了檢測到問題的機率,特別是對於將驗證視為常規維護任務的安裝。任何單個缺失或格式錯誤的元組在每次新的驗證嘗試中都有新的機會被檢測到。
amcheck
#amcheck
可有效檢測 資料校驗和 無法捕獲的各種故障模式。這些包括
由不正確的運算子類實現引起的結構不一致。
這包括作業系統排序規則發生變化的比較規則引起的問題。可排序型別(如 text
)的資料項比較必須是不可變的(就像 B-Tree 索引掃描使用的所有比較都必須是不可變的),這意味著作業系統排序規則規則永遠不能改變。雖然很少見,但作業系統排序規則的更新可能會導致這些問題。更常見的是,主伺服器和備用伺服器之間的排序順序不一致被牽連,可能是因為使用的主要作業系統版本不一致。這種不一致通常只會在備用伺服器上出現,因此通常只能在備用伺服器上檢測到。
如果出現此類問題,它可能不會影響每個使用受影響排序規則排序的索引,僅僅因為被索引的值可能碰巧具有相同的絕對排序,而不管行為不一致。有關 PostgreSQL 如何使用作業系統區域設定和排序規則的更多詳細資訊,請參閱 第 23.1 節 和 第 23.2 節。
索引與其被索引的堆關係之間的結構不一致(當執行 heapallindexed
驗證時)。
在正常操作過程中,不會對索引與其堆關係進行交叉檢查。堆損壞的症狀可能很微妙。
由底層 PostgreSQL 訪問方法程式碼、排序程式碼或事務管理程式碼中假設的未發現的錯誤引起的損壞。
對索引結構完整性的自動驗證在測試新功能或可能引入邏輯不一致的 PostgreSQL 功能的測試中起著作用。對錶結構以及相關的可見性和事務狀態資訊的驗證也起著類似的作用。一個明顯的測試策略是在執行標準迴歸測試時連續呼叫 amcheck
函式。有關執行測試的詳細資訊,請參閱 第 31.1 節。
檔案系統或儲存子系統故障(當停用資料校驗和時)。
請注意,如果訪問塊時只有共享緩衝區命中,amcheck
會檢查驗證時在某個共享記憶體緩衝區中表示的頁面。因此,amcheck
不一定會檢查驗證時從檔案系統中讀取的資料。請注意,當啟用校驗和時,amcheck
在將損壞的塊讀入緩衝區時可能會由於校驗和失敗而引發錯誤。
由於 RAM 故障或更廣泛的記憶體子系統故障引起的損壞。
PostgreSQL 不會防範可糾正的記憶體錯誤,並假定您將使用具有行業標準錯誤糾正碼 (ECC) 或更高級別保護的 RAM。然而,ECC 記憶體通常只能免疫單位元錯誤,不應假定能提供絕對的記憶體損壞故障防護。
當執行 heapallindexed
驗證時,通常會大大增加檢測單位元錯誤的機會,因為會測試嚴格的二進位制相等性,並且會測試堆中的已索引屬性。
結構損壞可能由於儲存硬體故障、關係檔案被覆蓋或被不相關軟體修改而發生。這種損壞也可以用 資料頁校驗和 檢測。
頁面格式正確、內部一致且其自身內部校驗和正確的關係頁面仍然可能包含邏輯損壞。因此,這種型別的損壞無法用校驗和檢測。例如,主表中的炸開值沒有在 toast 表中找到相應的條目,並且主表中的元組的事務 ID 早於資料庫或叢集中最舊的有效事務 ID。
在生產系統中觀察到多種邏輯損壞原因,包括 PostgreSQL 伺服器軟體中的錯誤、有缺陷且考慮不周的備份和恢復工具以及使用者錯誤。
損壞的關係在即時生產環境中是最令人擔憂的,而這些正是高風險活動最不受歡迎的環境。因此,verify_heapam
被設計為在沒有過度風險的情況下診斷損壞。它無法防範所有後端崩潰的原因,因為即使執行呼叫查詢也可能在嚴重損壞的系統上不安全。訪問目錄表時可能會出現問題,如果目錄本身已損壞,則可能會出現問題。
總的來說,amcheck
只能證明損壞的存在;它不能證明其不存在。
amcheck
報告的任何關於損壞的錯誤都不應是誤報。 amcheck
在發生按定義絕不應發生的情況時會引發錯誤,因此通常需要仔細分析 amcheck
錯誤。
沒有通用的方法來修復 amcheck
檢測到的問題。應尋找不變數違反的根本原因的解釋。 pageinspect 在診斷 amcheck
檢測到的損壞方面可能發揮有益的作用。 REINDEX
可能無法有效修復損壞。
如果您在文件中看到任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用此表格報告文件問題。