Skip to content

Instantly share code, notes, and snippets.

@ryogrid
Last active November 16, 2024 14:23
Show Gist options
  • Save ryogrid/7aa7feb669743dee4774b2389e7b2f2e to your computer and use it in GitHub Desktop.
Save ryogrid/7aa7feb669743dee4774b2389e7b2f2e to your computer and use it in GitHub Desktop.
postgresで各txnはアクセスすべきバージョンにどのように到達するか

PostgreSQLのMVCCとレコードバージョンの可視性(1)

MVCCでの並行性制御とバージョンの見つけ方

PostgreSQLのMVCC(Multi-Version Concurrency Control)では、各トランザクションが見るべきレコードのバージョンを効率的に見つける仕組みとして、以下のような方法が使われています。


基本的な概念

  1. Heap Tuple:

    • PostgreSQLでは、テーブルに格納される各行(レコード)はHeap Tupleとして管理されます。
    • Heap Tupleには、各トランザクションが使用するためのバージョン情報が含まれています。
  2. バージョン管理:

    • 各Heap Tupleには以下の情報が含まれています:
      • xmin: このレコードを作成したトランザクションID。
      • xmax: このレコードを削除または更新したトランザクションID(削除や更新前のレコードに記録される)。
      • 可視性マップ(Visibility Map)やFreeze情報なども補助的に使われます。
  3. トランザクションのスナップショット:

    • トランザクションは開始時にスナップショットを取得します。
    • スナップショットは「どのトランザクションがコミット済みで、どのトランザクションが未コミットか」を記録しています。

レコードバージョンの判定方法

トランザクションが対象レコードを検索する際、以下の手順で「見るべきバージョン」を判定します。

  1. xminxmaxのチェック:

    • スナップショットを基に、対象レコードのxminxmaxを調べます。
    • 条件:
      • 現在のトランザクションがxminのトランザクションを認識しており、それがコミット済みである。
      • xmaxが設定されていない、またはxmaxのトランザクションが未コミットである(つまり削除されていない)。
  2. トランザクションの状態を確認:

    • xminxmaxが指すトランザクションが現在どういう状態か(コミット済み、未コミット、またはアボート済みか)を確認します。
    • 必要に応じて、トランザクションログ(Transaction Log, 通称pg_clogpg_xact)を参照します。
  3. 可視性判定:

    • 判定条件に基づき、そのレコードバージョンが現在のトランザクションに「見える」かどうかを確認します。

効率化の仕組み

PostgreSQLでは、MVCCによる可視性チェックを効率化するために、いくつかの最適化が行われています。

  1. Visibility Map:

    • 各ページ(データブロック)ごとに「全てのレコードが可視である」かどうかを記録するビットマップ。
    • 全可視ページは細かいチェックをスキップできる。
  2. 行ロックと遅延削除(HOT Updates):

    • 同じページ内で更新されたレコードは特定のリンク構造(HOTチェイン)を持つため、関連レコードを効率的に追跡できます。
  3. Index-Only Scan:

    • インデックスだけで必要なデータを取得できる場合、Heap Tupleの可視性チェックを省略します。
  4. Freeze(フリーズ処理):

    • 古いトランザクションIDを「フリーズ」して、これ以上のチェックを不要にします。

まとめ

トランザクションが見るべきバージョンを見つけるプロセスは次のような流れです:

  1. Heap Tupleのxminxmaxを調査
  2. スナップショットに基づき可視性を判定
  3. 必要に応じて、トランザクションログや関連する最適化構造を参照。

これにより、トランザクション間での整合性を保ちながら、効率的なデータアクセスが可能になります。

PostgreSQLのMVCCとレコードバージョンの可視性(2)

インデックスのみでの参照について

インデックスが持つ情報

PostgreSQLのインデックスには、以下のような情報が格納されています:

  1. 検索キー(Index Key):

    • インデックスが対応する列の値。
  2. TID(Tuple ID):

    • インデックスエントリは各レコード(Heap Tuple)を指すTID(テーブル内の物理位置情報: ページ番号+行番号)を格納します。

インデックスはHeap Tupleの可視性情報(xminxmax)を持っていないため、通常、インデックスを使って対象のTIDを特定した後にHeap Tupleを参照して可視性チェックを行います。


インデックスのみでの操作(Index-Only Scan)

PostgreSQLは、Index-Only Scanという最適化を用いることで、インデックスのみを参照してデータを取得する場合があります。ただし、この手法が利用できるのは以下の条件を満たす場合です。

条件

  1. 必要なデータがすべてインデックスに格納されている:

    • 例えば、SELECT句で参照するカラムがインデックスに含まれる場合。
    • インデックスの値だけで結果が確定する場合(例: SELECT col1 FROM table WHERE col1 = 10)。
  2. 可視性チェックの効率化:

    • Visibility Mapを利用して、対象ページ内のすべてのレコードが可視であることが確認できる場合、Heap Tupleの参照をスキップできます。

実行例

以下のクエリはIndex-Only Scanが利用される可能性があります。

CREATE INDEX idx_col1 ON my_table (col1);

SELECT col1 FROM my_table WHERE col1 = 10;

この場合、col1の値とインデックスのTIDを使い、インデックスのみで結果を返せる場合があります。


完全にインデックスのみで可視性を保証するのが難しい理由

インデックスのみでは、以下の理由で完全な可視性チェックが難しい場合があります。

  1. MVCCの性質:

    • インデックスはxminxmaxといったバージョン管理情報を持っていないため、トランザクションのスナップショットに基づく可視性判定を行えません。
    • TIDを基にHeap Tupleを参照してxminxmaxを確認する必要があります。
  2. 更新や削除操作:

    • 更新されたレコードは新しいバージョンが別のHeap Tupleとして作成されるため、インデックスエントリは複数のバージョンを指す可能性があります(古いエントリがインデックス内に残っている場合がある)。
  3. HOT(Heap-Only Tuple)アップデート:

    • 同一ページ内で行われた更新(HOTアップデート)では、インデックスが新しいレコードを指さず、チェインでつながれたHeap Tupleを参照する必要があります。

まとめ

インデックスのみを使ってDBファイル内の位置やアクセスすべきバージョンを特定することは、特定の条件下(Index-Only Scanが有効な場合)で可能です。ただし、以下の限界があります。

  1. Heap Tupleが必要な可視性チェック:

    • 通常、インデックスを使ったアクセス後、Heap Tupleで詳細な可視性判定が行われます。
  2. MVCCの性質:

    • インデックスが可視性情報を持たないため、トランザクションのスナップショットに基づいた正確な判定にはテーブルデータの参照が不可欠。

将来的に、PostgreSQLのインデックス構造がさらに拡張されれば、可視性情報をインデックスに統合することも可能性としては考えられますが、現時点ではHeap Tupleの参照が多くの場合必要です。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment