amcheck
モジュールは、リレーションの構造の論理的な一貫性を検査する機能を提供します。
構造が適正であると見なされれば、エラーは報告されません。
この関数は、特定のリレーションの構造表現における様々な不変量を検査します。
インデックスの走査や、その他の重要な操作を担うアクセスメソッド関数の正しさは、これらの不変量を常に保つことに依存しています。
たとえば、ある関数は、とりわけすべてのB-Treeページの中の項目が「論理的な」順序になっていることを検査します。(たとえばtext
のB-Treeインデックスでは、インデックスタプルは語句の照合順になっていなければなりません。)
その特定の不変量が何らかの理由で保たれなければ、該当するページで二分探索が不正なインデックス走査をもたらし、SQL問い合わせに誤った答えを返すことになるでしょう。
検証は、インデックス走査自身で使われるのと同じ手続きを用いて行われます。 その手続きは、ユーザ定義演算子クラスのコードかもしれません。 たとえば、B-Treeインデックスの検査は、一つ以上のB-Treeサポート関数1ルーチンを用いる比較に依存しています。 演算子クラスサポート関数の詳細については37.16.3をご覧ください。
amcheck
関数はスーパーユーザだけが使用できます。
bt_index_check(index regclass, heapallindexed 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
の場合には、インデックス中のインデックスタプルに対応するすべてのヒープタプルの存在が検証されます、
実行中のプロダクション環境で定期的、軽量なデータ破損検査が必要な場合、bt_index_check
を使うのが、検査の完全性と、アプリケーションの性能と稼働への影響を限定することとの間の最良のトレードオフになることがしばしばあります。
bt_index_parent_check(index regclass, heapallindexed boolean, rootdescend boolean) returns void
bt_index_parent_check
は、ターゲットとなるB-Treeインデックスが様々な不変量を保っていることを検査します。
オプションとして、heapallindexed
引数がtrue
の場合、インデックスに対応して存在すべきすべてのヒープタプルの存在を検証します。
省略可能な引数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
は、ホットスタンバイモードが有効な場合(すなわち、読み出し専用物理レプリカ)では使用できません。
bt_index_check
とbt_index_parent_check
は両方とも、DEBUG1
とDEBUG2
の深刻度レベルで検証プロセスに関するログメッセージを出力します。
このメッセージは、PostgreSQL開発者にとって興味のあるかもしれない検証プロセスに関する詳細な情報を提供します。
検証が実際に非一貫性を検出する追加の状況を提供しますので、上級ユーザにもこの情報は役立つかもしれません。
検証問い合わせを実行する前に対話式のpsqlセッションで
SET client_min_messages = DEBUG1;
を実行すると、扱いやすい程度の詳細で検証の進行状況に関するメッセージを表示します。
heapallindexed
検証
検証関数のheapallindexed
引数がtrue
ならば、ターゲットのインデックスリレーションと関連付けられたテーブルに対して追加の検証フェーズが実施されます。
これは「ダミー」のCREATE INDEX
操作から構成され、インメモリ上の一時的なサマリー構造(これは必要に応じて基礎的な最初の検証フェーズで構築されます)に対する仮想的な新しいインデックスタプルがすべて存在することをチェックします。
サマリー構造はターゲットのインデックスで見つかったすべてのタプルに対して「指紋採取(fingerprint)」を行います。
heapallindexed
検証の背後にある高レベルの原理は、新しいインデックスが既存のインデックスと等しいこと、ターゲットインデックスが既存の構造中に見つかったエントリーのみを含むことです。
追加のheapallindexed
フェーズは大きなオーバーヘッドをもたらします。
典型的には、検証に数倍時間かかるようになります。
しかし、取得されたリレーションレベルのロックに対して、heapallindexed
検証が実施されるときに変更はありません。
サマライズ構造は、maintenance_work_mem
によってその大きさが制限されます。
インデックス中に存在すべきヒープタプルの非一貫性の検出失敗の確率が2%を超えないことを保証するために、タプルごとに約2バイトのメモリが必要です。
タプルごとに利用可能なメモリが少ないほど、非一貫性を見逃す可能性が徐々に増えていきます。
この手法によって検証のオーバーヘッドを大幅に減らせる一方、とりわけ検証を日常的な保守作業として行うシステムでは、問題を検出できる確率が少し減少するだけです。
失われた、あるいは不正なタプルは次の検証の機会に検出されます。
amcheck
を効果的に使う
amcheck
は、データページチェックサムが検知できないような、様々なタイプの障害モードを効果的に検知できます。
以下のようなものがあります。
演算子クラスの正しくない実装によって引き起こされる構造の非一貫性。
オペレーティングシステムの照合順の比較ルールの変更による問題も含まれます。
text
のような照合可能な型のデータの比較は、不変でなけれならず(B-Treeインデックスの走査のための、すべての比較が不変でなければならないのと同じことです)、それはオペレーティングシステムの照合順が決して変化してはいけないことを意味します。
まれであるとは言え、オペレーティングシステムの照合順ルールの更新は、これらの問題を引き起こします。
もっと普通に起こることとしては、マスターサーバとスタンバイサーバの照合順の違いが関与することです。
これは、使用されているオペレーティングシステムのメジャーバージョンに一貫性がないことによります。
そうした一貫性の欠如は一般的にスタンバイサーバでのみ起こるので、通常スタンバイサーバでのみ検出されます。
そうした問題が起きても、影響を受けた照合順を使って順序付けられた個々のインデックスには影響ないかもしれません。 これは単純に、振る舞いにおける一貫性のなさにかかわらずインデックスされた値は同じ絶対的な順になるからです。 PostgreSQLがオペレーティングシステムのロケールと照合順をどう使用するかについての更なる詳細については、23.1と23.2をご覧ください。
インデックスとインデックス付されたヒープリレーションの間の構造的な非一貫性(heapallindexed
検証が実施される場合)
通常の操作においてはインデックスとそのヒープリレーションの間にはクロスチェックはありません。 ヒープの破壊による症状は些細なものかもしれません。
依拠するPostgreSQLのアクセスメソッド、あるいはソート、トランザクション管理コードにおける、潜在的なまだ見つかっていないバグによる破損。
新規、あるいは提案中の PostgreSQLの機能が、論理的な非一貫性をもたらしかねないかどうか全般的にテストする際に、インデックスの構造的な一貫性の自動検証が役立ちます。
テーブル構造と、関連する可視性およびトランザクション状態情報の検証も同じような役割を果たします。
わかりやすいテスト戦略の一つは、関数標準の回帰テストを実行中に、amcheck
を継続的に呼び出すことです。
テストの実行に関する詳細は、32.1をご覧ください。
単にチェックサムが有効になっていないファイルシステムあるいはストレージサブシステムの障害。
amcheck
は、ブロックをアクセスする際に共有バッファがヒットした場合、検査時に共有メモリバッファ上に表現されたページを調査します。
結果として、amcheck
は、検査時にファイルシステムから読み込んだデータを調査するとは限りません。
チェックサムが有効な場合、amcheck
は壊れたブロックをバッファに読み込んだ際にチェックサム障害によるエラーを報告するかもしれません。
欠陥のあるRAMあるいは、広範囲に渡るメモリサブシステムによる破損。
PostgreSQLは、訂正可能なメモリーエラーからは身を守らないので、業界標準のエラー訂正コード(ECC)、あるいはもっと優れた保護機構を使ったRAMを使って運用する前提となっています。 しかし、ECCメモリは典型的には単一ビットエラーに対してのみ耐性があり、メモリ破損に起因する障害に対して完全な保護を提供すると考えるべきではありません。
heapallindexed
検証が実施されると、一般に1ビットエラーを検出する可能性が非常に高くなります。
これは、バイナリ一致を厳密にテストすることと、またヒープ内のインデックス付けされたアトリビュートをテストすることによります。
一般的に、amcheck
は破損の存在を証明することはできますが、破損がないことを証明することはできません。
amcheck
が報告するエラーが関与する破損で、偽陽性のものはありません。
amcheck
は、定義により発生してはならないはずの条件下で発生したエラーを報告するので、amcheck
の報告するエラーを注意深く解析することがしばしば求められます。
amcheck
が検出した問題を修復する一般的な方法はありません。
不変条件違反の根本的な原因の説明が求められます。
amcheck
が検出した破損の診断には、pageinspectが有用な役割を担うかもしれません。
REINDEX
は破損の修復には効果的ではないかもしれません。