ぱと隊長日誌

ブログ運用もエンジニアとしての生き方も模索中

PostgreSQL のシリアライザブル分離レベルにおけるスナップショットのタイミング

PostgreSQL 10 のマニュアルには以下の記載があります。

13.2.2. リピータブルリード分離レベル
リピータブルリードのトランザクション内の問い合わせは、トランザクション内の現在の文の開始時点ではなく、トランザクションの最初のトランザクション制御以外の文の開始時点のスナップショットを見る、という点でこのレベルはリードコミッティドと異なります。 従って、単一トランザクション内の連続するSELECT文は、同じデータを参照します。つまり、自身のトランザクションが開始した後にコミットされた他のトランザクションによる変更を参照しません。

13.2.3. シリアライザブル分離レベル
実際、この分離レベルは、(ある時点で)逐次実行可能なすべてのトランザクションにおいて、シリアライザブルトランザクションの同時実行の組が一貫性のないような振る舞いをしていないか監視することを除き、リピータブルリードと全く同じ動きをします。

13.2. トランザクションの分離

この記載から、PostgreSQL のシリアライザブル分離レベルのスナップショットはリピータブルリード分離レベルと同様に「トランザクションの最初のトランザクション制御以外の文の開始時点のスナップショットを見る」ということが推測できますし、実際にそのようにふるまいます。例を挙げて確認します。

検証は PostgreSQL 10.3 で行っています。
トランザクションの略称として"TX"を用います。

(1)先行TX(TX1)のBEGIN直後に後続TX(TX2)が割り込んだ場合

-- TX1

=# CREATE TABLE valtab(value integer);

=# INSERT INTO valtab values (1);

=# BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- TX2

=# BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;

=# SELECT * FROM valtab;
 value
-------
     1
(1 row)

=# UPDATE valtab SET value = 2;

=# COMMIT;
-- TX1

=# SELECT * FROM valtab;
 value
-------
     2
(1 row)

=# COMMIT;

TX1のBEGIN直後にTX2によってUPDATE/COMMITした内容を参照しました。つまり、シリアライザブル分離レベルにおいても参照するスナップショットはBEGIN時点ではないとわかります。

(2)先行TX(TX1)の操作後に後続TX(TX2)が割り込んだ場合

-- TX1

=# CREATE TABLE valtab(value integer);

=# INSERT INTO valtab values (1);

=# BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;

=# SELECT * FROM valtab;
 value
-------
     1
(1 row)
-- TX2

=# BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;

=# SELECT * FROM valtab;
 value
-------
     1
(1 row)

=# UPDATE valtab SET value = 2;

=# COMMIT;
-- TX1

=# SELECT * FROM valtab;
 value
-------
     1
(1 row)

=# COMMIT;

TX1のBEGIN直後のSELECT結果とTX2割り込み後のSELECT結果が同じとなっています(シリアライザブル分離レベルなので当然ではあります)。シリアライザブル分離レベルにおいても「トランザクションの最初のトランザクション制御以外の文の開始時点のスナップショットを見る」ことが分かりました。