同時実行制御。スナップショット
スナップショットに関する宿題の答え
- データが複数ページにまたがっている場合、次のResultSet#next()で次のページを読む前に別のトランザクションによってデータが変えられたどうなる?文レベルの読み取り一貫性があるのなら、書き換わる前のデータが読めるはず?
- YES。書き換わる前のデータが読める(READ COMMITEDの分離レベル以上で)。
- MVCCのスナップショットで実現?
- YES。
- スナップショットってどんなデータ?
- PostgreSQLにおけるスナップショットはデータのコピーではなく、トランザクションの可視性を決定するために使用するいくつかのトランザクションID。タプルはタプルを作成したトランザクションのIDや削除したトランザクションのIDをもっているので、スナップショットさえあれば、トランザクションIDを比較することであるトランザクションから見えるべきタプルかそうでないかを判断できる。なぜ、データのコピーをする必要がないかといえば、PostgreSQLでは、更新しても削除しても元のタプルが(VACUUMするまでは)残っているから。まだ読まれるかもしれないタプルはVACUUMの対象にならないようになっているはず。スナップショットはSnapshotData構造体で表される。
SnapshotData構造体
タプルの可視性をチェックするための関数ポインタやトランザクションIDやコマンドIDをもつ。タプルの可視性をチェックするための関数の代表格は、HeapTupleSatisfiesMVCCだと思う。トランザクションIDやコマンドIDの使った可視性の判定方法をもう少し調べる。
SerializableSnapshotとLatestSnapshot
SerializableSnapshotは、トランザクションの中で一度だけ取得される。LatestSnapshotは問い合わせのたびに取得される。READ COMMITEDのクエリではLatestSnapshotが使われる。REPETABLE READ以上ではSerializableSnapshotが使われる(REPETABLE READとSERIALIZABLEで区別がない?)。これらのどちらかのコピーがActiveSnapshotに入れられて使われているよう。
この前のJDBCの例で使ったPreparedStatementを使った例だと、Perseのメッセージ処理の中(関数でいうとpg_plan_queries)でSerializableSnapshotが取得され、Bindのメッセージ処理の中(関数でいうとPortalStart)でLatestSnapshotが取得される。
SerializableSnapshotは必ず作成されるので、分離レベルがSERIALIZBLEだからといって重たい処理が走るわけではなさそう(トランザクションが長ければ長いほど更新の競合が発生しやすくなり、同時実行性が落ちるということはありそうだけど)。