トランザクションの分離 から ページ:
EXPLAIN
そのSELECT
クエリプランがどのように実行されているかを知ることができますが、テーブルが小さい(または空である!)場合、PostgreSQLはインデックスを参照する代わりにシーケンシャルスキャンをほぼ確実に選択します。これにより、テーブル全体で述語ロックが発生し、別のトランザクションがテーブルに対して何かを実行するたびにシリアル化が失敗します。
私のシステム:
isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
QUERY PLAN
----------------------------------------------------------
Seq Scan on mydevice (cost=0.00..23.38 rows=5 width=46)
Filter: (cid = 1)
(2 rows)
インデックスを追加して、それを強制的に使用することができます:
isolation=# CREATE INDEX mydevice_cid_key ON mydevice (cid);
CREATE INDEX
isolation=# SET enable_seqscan = off;
SET
isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
QUERY PLAN
----------------------------------------------------------------------------------
Index Scan using mydevice_cid_key on mydevice (cost=0.00..8.27 rows=1 width=46)
Index Cond: (cid = 1)
(2 rows)
ただし、これは正しい解決策ではありません。少しバックアップしましょう。
シリアル化可能とは、実際にこれらのトランザクションを同時に実行しているにもかかわらず、トランザクションが次々に実行された場合とまったく同じ効果を持つことを保証することを意味します。 PostgreSQLには無限のリソースがないため、クエリが実際にアクセスするデータに述語ロックをかけるのは事実ですが、「データ」は「返された行」以上のものを意味する場合があります。
PostgreSQLは、問題があると判断した場合、確実な場合ではなく、シリアル化の失敗にフラグを立てることを選択します。 (したがって、行ロックをページロックに一般化する方法です。)この設計上の選択により、例のような誤検知が発生します。誤検知は理想的とは言えませんが、分離セマンティクスの正確性には影響しません。
エラーメッセージは次のとおりです。
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.
そのヒントが鍵となります。アプリケーションはシリアル化の失敗をキャッチし、操作全体を再試行する必要があります 。これは、SERIALIZABLE
の場合は常に当てはまります。 実行中です-並行性にもかかわらずシリアルの正確性を保証しますが、アプリケーションの助けがなければそれを行うことはできません。言い換えると、実際に同時変更を行っている場合、PostgreSQLが分離要件を満たすことができる唯一の方法は、アプリケーションにそれ自体をシリアル化するように依頼することです。したがって: