PostgreSQL amcheck

2021-09-16 15:25 更新
F.2.1. 函數(shù)
F.2.2. 可選的heapallindexed驗(yàn)證
F.2.3. 有效地使用amcheck
F.2.4. 修復(fù)損壞

amcheck 模塊提供的函數(shù)讓用戶能驗(yàn)證關(guān)系結(jié)構(gòu)的邏輯一致性。如果結(jié)構(gòu)有效,則不會(huì)發(fā)生錯(cuò)誤。

這些函數(shù)驗(yàn)證特定關(guān)系的結(jié)構(gòu)表達(dá)中的各種不變條件。索引掃描以及其他重要操作背后的訪問方法的正確性都要依仗這些不變條件的成立。例如,在這些函數(shù)中,有一些負(fù)責(zé)驗(yàn)證所有B樹頁面中的項(xiàng)都按照邏輯順序(比如,對于text上的B樹索引,索引元組應(yīng)該按照詞典順序排列)擺放。如果特定的不變條件由于某種原因無法成立,則我們可以預(yù)料受影響頁面上的二分搜索將無法正確地引導(dǎo)索引掃描,最終導(dǎo)致SQL查詢得到錯(cuò)誤的答案。

驗(yàn)證過程采用索引掃描自身使用的同種過程來執(zhí)行,這些過程可能是用戶定義的操作符類代碼。例如,B樹索引驗(yàn)證依賴于由一個(gè)或者多個(gè)B樹支持函數(shù)1例程構(gòu)成的比較。操作符類支持函數(shù)的詳情請見第 37.16.3 節(jié)。

amcheck函數(shù)只能由超級用戶使用。

F.2.1. 函數(shù)

bt_index_check(index regclass, heapallindexed boolean) returns void

bt_index_check測試一個(gè)B樹索引,檢查各種不變條件。用法實(shí)例:

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)

這個(gè)例子中的會(huì)話執(zhí)行對數(shù)據(jù)庫test中10個(gè)最大目錄索引的驗(yàn)證。對于唯一索引會(huì)要求驗(yàn)證堆元組是否有對應(yīng)的索引元組存在。由于沒有錯(cuò)誤報(bào)出,所有的被測索引都處于邏輯一致的狀態(tài)。自然地,很容易將這個(gè)查詢改為對支持驗(yàn)證的數(shù)據(jù)庫中的每一個(gè)索引調(diào)用bt_index_check。

bt_index_check要求目標(biāo)索引及其所屬的堆關(guān)系上的AccessShareLock。這種鎖模式與簡單SELECT語句在關(guān)系上所要求的鎖模式相同。bt_index_check不驗(yàn)證跨越父子關(guān)系的不變條件,但是在heapallindexedtrue時(shí)將驗(yàn)證所有堆元組是否作為索引中的索引元組存在。當(dāng)在生產(chǎn)環(huán)境中要求一個(gè)使用bt_index_check的例程進(jìn)行輕量化損壞測試時(shí),它常常需要在驗(yàn)證徹底性和減小對應(yīng)用性能及可用性的影響之間做出權(quán)衡。

bt_index_parent_check(index regclass, heapallindexed boolean, rootdescend boolean) returns void

bt_index_parent_check測試一個(gè)B樹索引,檢查多種不變條件。 可選地,當(dāng)heapallindexed參數(shù)為true時(shí),該函數(shù)驗(yàn)證所有應(yīng)該在索引中找到的堆元組的存在。 當(dāng)可選參數(shù)rootdescend值為 true時(shí),對于每個(gè)元組,驗(yàn)證程序通過從根頁面執(zhí)行新的搜索來重新查找葉子層級的元組。bt_index_parent_check能夠執(zhí)行的檢查是bt_index_check能執(zhí)行的檢查的超集。 bt_index_parent_check可以被想成是bt_index_check的一種更全面的變體:和 bt_index_check不同,bt_index_parent_check還檢查跨越父/子關(guān)系的不變條件,包括檢查索引結(jié)構(gòu)中是否沒有缺失的下鏈。 如果找到邏輯不一致或者其他問題,bt_index_parent_check遵循通常的報(bào)錯(cuò)習(xí)慣。

bt_index_parent_check要求目標(biāo)索引上的一個(gè)ShareLock(還要求對關(guān)系上的一個(gè)ShareLock)。這些鎖阻止來自INSERTUPDATE以及DELETE命令的并發(fā)數(shù)據(jù)修改。這些鎖同時(shí)防止底層關(guān)系被并發(fā)的 VACUUM以及其他工具命令處理。注意該函數(shù)只在其運(yùn)行期間而不是整個(gè)事務(wù)期間持有鎖。

bt_index_parent_check的額外驗(yàn)證更有可能檢測到多種病態(tài)的情況。這些情況可能涉及到被查索引使用的一種不正確實(shí)現(xiàn)的B-樹操作符類,或者說不定是底層B-樹索引訪問方法代碼中未被發(fā)現(xiàn)的缺陷。注意與bt_index_check不同,當(dāng)熱備模式被啟用時(shí)(即在只讀的物理復(fù)制機(jī)上)不能使用bt_index_parent_check。

提示

bt_index_checkbt_index_parent_check 都輸出關(guān)于驗(yàn)證過程的日志信息,在DEBUG1DEBUG2 嚴(yán)重性級別。 這些消息提供關(guān)于驗(yàn)證過程的詳細(xì)信息,或許對PostgreSQL的開發(fā)人員有作用。 高級用戶也許會(huì)發(fā)現(xiàn)這些信息很有幫助,因?yàn)樗峁┝祟~外的上下文將驗(yàn)證實(shí)際檢測的不一致。運(yùn)行:

SET client_min_messages = DEBUG1;

在運(yùn)行驗(yàn)證查詢之前的交互式psql會(huì)話中,將顯示有關(guān)驗(yàn)證進(jìn)度的消息,并具有可管理級別的詳細(xì)信息。

F.2.2. 可選的heapallindexed驗(yàn)證

當(dāng)驗(yàn)證函數(shù)的heapallindexed參數(shù)為true時(shí),會(huì)針對與目標(biāo)索引關(guān)系關(guān)聯(lián)的表執(zhí)行一個(gè)額外的驗(yàn)證過程。這種驗(yàn)證由一個(gè)假的CREATE INDEX操作組成,它針對一個(gè)臨時(shí)的、內(nèi)存中的匯總結(jié)構(gòu)(根據(jù)需要在基礎(chǔ)的第一階段驗(yàn)證過程中建立)檢查所有假想的新索引元組的存在。這個(gè)匯總結(jié)構(gòu)對目標(biāo)索引中的每一個(gè)元組 采集指紋。heapallindexed驗(yàn)證背后的高層原則是:等效于現(xiàn)有目標(biāo)索引的新索引必須僅擁有能在現(xiàn)有結(jié)構(gòu)中找得到的項(xiàng)。

額外的heapallindexed階段會(huì)增加明顯的開銷:驗(yàn)證的時(shí)間通常將會(huì)延長幾倍。不過,在執(zhí)行heapallindexed驗(yàn)證時(shí),所要求的關(guān)系級鎖沒有變化。

這一匯總結(jié)構(gòu)的尺寸以maintenance_work_mem為界。為了確保對于每個(gè)堆元組應(yīng)該存在于索引中這一檢測有不超過2%的失效概率能檢測到不一致,每個(gè)元組需要大約2個(gè)字節(jié)的內(nèi)存。因?yàn)槊總€(gè)元組可用的內(nèi)存變少,錯(cuò)失一處不一致的概率就會(huì)慢慢增加。這種方法顯著地限制了驗(yàn)證的開銷,但僅僅略微降低了檢測到問題的概率,對于將驗(yàn)證當(dāng)作例行維護(hù)任務(wù)的安裝來說更是如此。對于每一次新的驗(yàn)證嘗試,任何單一的缺失或者畸形元組都有新的機(jī)會(huì)被檢測到。

F.2.3. 有效地使用amcheck

amcheck對于檢測多種數(shù)據(jù)頁面校驗(yàn)和無法捕捉到的失效模式非常有效。包括:

  • 由不正確的操作符類實(shí)現(xiàn)導(dǎo)致的結(jié)構(gòu)性不一致。

    這包括操作系統(tǒng)排序規(guī)則的比較規(guī)則變化導(dǎo)致的問題。text之類的可排序類型數(shù)據(jù)的比較必須是不變的(正如用于B-樹索引掃描的所有比較必須不變一樣),這意味著操作系統(tǒng)排序規(guī)則必須保持不變。但是在很少的情況下,操作系統(tǒng)排序規(guī)則的更新會(huì)導(dǎo)致這些問題。更常見的,主服務(wù)器和后備服務(wù)器之間排序順序的不一致會(huì)相互牽連,這可能是因?yàn)槭褂玫?span id="w0cvkd6" class="emphasis">操作系統(tǒng)版本不一致。這類不一致通常僅出現(xiàn)在后備服務(wù)器上,因此通常也僅能在后備服務(wù)器上檢測到。

    如果這類問題出現(xiàn),則它可能不會(huì)影響使用受影響排序規(guī)則排序的每一個(gè)索引,其原因是被索引值可能正好具有與行為不一致無關(guān)的相同的絕對順序。關(guān)于PostgreSQL如何使用操作系統(tǒng)locale和排序規(guī)則的進(jìn)一步細(xì)節(jié)請參考第 23.1 節(jié)第 23.2 節(jié)。

  • 索引和被索引的對關(guān)系之間的結(jié)構(gòu)不一致(在執(zhí)行heapallindexed驗(yàn)證時(shí))。

    在普通操作時(shí)沒有將索引針對其對關(guān)系進(jìn)行交叉檢查。堆損壞的癥狀可能是很微妙的。

  • 由于底層PostgreSQL訪問方法代碼、排序代碼或者事務(wù)管理代碼中(假想的)未發(fā)現(xiàn)的缺陷導(dǎo)致的損壞。

    在測試可能引入邏輯不一致的PostgreSQL新特性或者被提議的特性時(shí),索引的結(jié)構(gòu)完整性自動(dòng)驗(yàn)證扮演了重要角色。表結(jié)構(gòu)、相關(guān)的可見性和事務(wù)狀態(tài)信息的驗(yàn)證扮演了類似的角色。一種顯而易見的測試策略是在運(yùn)行標(biāo)準(zhǔn)回歸測試時(shí)持續(xù)地調(diào)用amcheck函數(shù)。運(yùn)行這些測試的詳情請參考第 32.1 節(jié)

  • 正巧沒有開啟校驗(yàn)和的文件系統(tǒng)或者存儲(chǔ)子系統(tǒng)故障。

    注意,如果在訪問塊時(shí)僅有一次共享緩存命中,驗(yàn)證時(shí)amcheck會(huì)在檢查表示在某個(gè)共享內(nèi)存緩沖區(qū)中的頁面。因此,amcheck沒有必要在驗(yàn)證時(shí)檢查從文件系統(tǒng)讀出的數(shù)據(jù)。注意當(dāng)校驗(yàn)和被啟用時(shí),如果一個(gè)損壞的塊被讀取到緩沖區(qū)中,amcheck可能會(huì)由于校驗(yàn)和失效而產(chǎn)生錯(cuò)誤。

  • 有缺陷的RAM或者內(nèi)存子系統(tǒng)導(dǎo)致的損壞。

    PostgreSQL無法提供針對可更正內(nèi)存錯(cuò)誤的保護(hù)并且它假定用戶使用的是具有工業(yè)標(biāo)準(zhǔn)糾錯(cuò)碼(ECC)或更好保護(hù)技術(shù)的RAM。不過,ECC內(nèi)存通常只能免疫單個(gè)位錯(cuò)誤,并且不應(yīng)該假定它能提供對導(dǎo)致內(nèi)存損壞失效的絕對保護(hù)。

    在執(zhí)行heapallindexed驗(yàn)證時(shí),通常有大幅增加的機(jī)會(huì)可以檢測單個(gè)位錯(cuò)誤,因?yàn)闀?huì)測試嚴(yán)格的二元等值并且會(huì)在堆中測試被索引屬性。

通常,amcheck僅能證明損壞的存在,但它無法證明損壞不存在。

F.2.4. 修復(fù)損壞

amcheck沒有產(chǎn)生與損壞相關(guān)的錯(cuò)誤絕不應(yīng)該被當(dāng)做假陽性。amcheck會(huì)在(定義上)應(yīng)該絕不會(huì)發(fā)生的情況中拋出錯(cuò)誤,因此常常需要對amcheck錯(cuò)誤進(jìn)行仔細(xì)地分析。

對于amcheck檢測到的問題沒有一般性的修復(fù)方法。應(yīng)該尋找產(chǎn)生不變條件違背的根本原因。在診斷amcheck檢測到的損壞時(shí),pageinspect可能會(huì)扮演一個(gè)非常有用的角色。REINDEX在修復(fù)損壞過程中可能無法起到效果。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)