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