テーブルを作成した場合。
CREATE TABLE foo AS
SELECT CASE WHEN random() > 0.5 THEN x END AS x
FROM generate_series(1,10) AS x;
そして、私はトランザクションで以下を実行します
BEGIN;
SELECT count(*)
FROM foo
WHERE x IS NOT NULL;
--time
SELECT count(*)
FROM foo
WHERE x IS NOT NULL;
END;
どのトランザクションレベルの下で、私の結果がトランザクション内で同じであることが保証されますか?
私の10セント、そして PostgreSQLドキュメント に基づく:
仮定:独自のトランザクションは、テーブルfooから関連する値を変更しません。
両方のクエリで同じcount(*)
を使用するということは、次のことを意味します。
別のトランザクションが最初の選択と2番目の選択の間のfooテーブルにさらに行を書き込んだ可能性があるため、_dirty reads
_は使用できません。あなたはそれらを見るべきではありません。
実際には列を読み取っていないので、_nonrepeatable reads
_は問題ではありません。
_phantom reads
_を含めることはできません。つまり、別のトランザクションがx
列がNULL
である行をnull以外の値に変更した場合、トランザクションはそれらの変更に影響を与えることに気づく必要はありませんWHERE
条件。
_serialization anomalies
_の判断方法がよくわかりません。私の学んだ推測では、これは必要ありません。しかし、これは本当に議論の余地があります。
これらの条件下で、 トランザクション分離レベル の表は、_Repeatable read
_が以下の基準に準拠していることを明確にします。
...そのため、両方の選択ステートメントで同じcount(*)
が得られます。
「トランザクション内」で同じであることが保証されています
これには2つの意味があり、それが問題です。トランザクションの外側で何が起こっているのか、「スナップショット」で内側で何が起こっているのかがわかります。 count(*)
の結果には、次の2つのことが起こります。
READ COMMITTED
_(および_READ UNCOMMITTED
_†)。REPEATABLE READ
_およびSERIALIZABLE
。変更することと変更しないことは、あなたが知る必要があるすべてではありません。 _REPEATABLE READ
_およびSERIALIZABLE
はsnapshotsで機能します。つまり、counts(*)
は変更されませんが、データベースですでに変更されている可能性があるものに関しては何も意味しません。
いくつかを簡略化して確認し、上記の初期化コードREINIT
を呼び出します。ここでは、これら2つのステートメントのみを扱います。
SELECT count(*) FROM foo WHERE x IS NULL;
UPDATE foo SET x = 1 WHERE x IS NULL;
_ここで、2つのセッションを実行するとします。
_REINIT
1# BEGIN; SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
1# SELECT count(*) FROM foo WHERE x IS NULL;
2# UPDATE foo SET x = 1 WHERE x IS NULL;
1# SELECT count(*) FROM foo WHERE x IS NULL;
_
次に、count(*)
は_1#
_のトランザクションで何を表示しますか?そして、両方のトランザクションがコミットした後、_#1
_、次に_#2
_?スポイラー警告:
トランザクションでは、同じ番号が表示されます。
UPDATE
はすでにコミットされているため、トランザクション以外では0が表示されます。
これで、トランザクションレベルが低くなると、_REPEATABLE READ
_のように、_READ COMMITTED
_のように、2番目のSELECT
by _#1
_がコミットされた行を表示します。そして、より高いトランザクションレベルで。最初のSELECT count(*)
は、_x IS NULL
_である行に対してのみ述語ロックを取得します。では、レベルをSERIALIZABLE
に上げて同じシーケンスを実行するとどうなるでしょうか(述語ロック付き)。
_REINIT
1# BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
1# SELECT count(*) FROM foo WHERE x IS NULL;
2# UPDATE foo SET x = 1 WHERE x IS NULL;
1# SELECT count(*) FROM foo WHERE x IS NULL;
_
スポイラー警告:
何もない。同じこと。
どうして? 同時実行モデルはUPDATE
sを考慮しませんnless何かがそれらの行を変更しようとし、両方がコミットすることを意図しています。両方ともSELECT
ingであり、スナップショットを変更していない。ひいては、
_if (SELECT count(*)
FROM foo
WHERE x IS NULL
) < arg THEN
RETURN 0 ;
end if ;
_
READ COMMITTED
_で、残りのトランザクションとは異なる数値が表示される場合があります。2番目のSELECT
間で同時トランザクションがコミットされた可能性があります。そのため、_REPEATABLE READ
_またはSERIALIZABLE
を使用しないと役に立たなくなります。REPEATABLE READ
_でロックの問題が発生するか、トランザクションの外部から他に何かがテーブルにアクセスすると、SERIALIZABLE
でのコミット時にエラーが発生する可能性があります。繰り返しますが、限られたポイントがあります。 count(*)
クエリが行を返した場合、たとえば、他のコードはそれらの行を更新して、それらが既に変更されているか、作業中のスナップショットのときにもう存在しないことを見つけようとする場合があります。コミットします。したがって、条件付きでトランザクションを実行しないでください。何かをして、それを処理します。
†SQL標準では、レベル_READ UNCOMMITTED
_が規定されています。 PostgreSQLでは そのレベルのエイリアス それはより厳しい分離レベル_READ COMMITTED
_です。