50.4. インデックスのロック処理に関する検討

インデックスアクセスメソッドは、複数のプロセスによるインデックスの同時更新を取り扱えなければなりません。 PostgreSQL中核システムはインデックススキャン中にインデックスに対してAccessShareLockを獲得します。 また、(通常のVACUUMを含む)インデックスの更新中にRowExclusiveLockを獲得します。 これらのロック種類は競合しませんので、アクセスメソッドは必要になるかもしれない微妙な粒度のロックの扱いに関して責任を持ちます。 インデックスの生成、破棄、REINDEXVACUUM FULL時にインデックス全体に対する排他ロックが獲得されます。

同時更新をサポートするインデックス種類を構築することは通常、必要な動作について広範かつ微細にわたる解析が必要です。 b-tree、およびハッシュインデックス種類では、src/backend/access/nbtree/READMEsrc/backend/access/hash/READMEにある設計に関する決定事項を読むことができます。

インデックス自身の内部的な一貫性要求の他に、同時実行更新には、親テーブル(ヒープ)とインデックス間の一貫性に関する問題が発生します。 PostgreSQL はヒープへのアクセスおよび更新とインデックスへのアクセスと更新を分離していますので、インデックスとヒープとの間の一貫性が無くなる間隔が存在します。 以下の規則でこうした問題を扱います。

3番目の規則がないと、VACUUMによって削除される直前に、インデックス読み取りがインデックス項目を見つけ、そして、VACUUMによって削除された後に対応するヒープ項目に達する可能性があります。 空の項目スロットがheap_fetch()で無視されますので、これはその項目番号が読み取りが達した時に未使用である場合、軽微な問題を引き起こします。 しかし、第三のバックエンドがすでにその項目スロットを他のものに再使用した場合はどうなるでしょうか? そのスロット内の新しいものが、スナップショット試験を通過するには新しすぎることが確実ですので、MVCCに則ったスナップショットを使用する場合は問題ありません。 しかし、MVCCに則らないスナップショット(SnapshotNowなど)では、実際にはスキャンキーに合わない行を受付け、返す可能性があります。 すべての場合においてヒープ行に対しスキャンキーの再検査を行うことを必須とすることで、こうした状況から保護することができますが、これは高価すぎます。 代わりに、読み取りがまだ一致するヒープ項目へのインデックス項目の"作業中" であることを示す代理として、インデックスページに対するピンを使用します。 このピンに対してambulkdeleteがブロックするようにすることで、読み取りの作業が終わる前にVACUUMがそのヒープ項目を削除できないことを確実にします。 実行時におけるこの対策のコストは小さく、実際に競業が発生するごく稀な場合にのみブロックするためのオーバーヘッドが加わります。

この対策は、インデックススキャンが"同期"していることを要求します。 対応するインデックス項目のスキャンの後即座に各ヒープタプルを取り出さなければなりません。 多くの理由があり、これは高価です。 インデックスから多くのTIDを収集し、少し後でのみヒープタプルにアクセスする"非同期"スキャンでは、必要なロック処理オーバーヘッドがかなり少なくなり、また、より効率的なヒープへのアクセスパターンを取ることができます。 上の解析に従うと、 MVCCに則らないスナップショットでは同期方式を使用しなければなりませんが、問い合わせがMVCCスナップショットを使用する場合は非同期スキャンを使用することができます。

amgetmultiインデックススキャンでは、アクセスメソッドは返されるタプル上のインデックスピンを保持することを保証する必要はありません。 (直前のもの以上をピンすることは非現実的です。) したがって、MVCCに則ったスナップショットでこうしたスキャンを使用することのみが安全です。

アダルトレンタルサーバー