6

テーブルと、を指す外部キーを持つfoo_bar別のテーブルがあります。関連する行が削除されると、行はカスケード削除されます。spam_eggsfbfoo_barspam_eggsspam_eggs.fb

私はPostgreSQLを使用しています。

トランザクションでは、行SELECT... FOR UPDATEをロックするために使用しました。spam_eggsこのトランザクションの期間中に、別のトランザクションが私のロックされた行DELETE FROM...の関連を試みました。foo_barこれによりエラーがトリガーされますか、それとも元の更新トランザクションが終了するまで行がロックされるとクエリがブロックされますか?

4

1 に答える 1

14

試してみてください。開いpsqlてセットアップを行います。

CREATE TABLE foo_bar(id integer primary key);
CREATE TABLE spam_eggs(
     foo_bar_id integer not null references foo_bar(id) on delete cascade
);
INSERT INTO foo_bar (id) VALUES (1),(2),(3),(4);
INSERT INTO spam_eggs(foo_bar_id) VALUES (1),(2),(3),(4);

次に、別のpsql接続を開きます。BEGINそれらの両方でのトランザクション。

  1. 最初の(古い)セッションで、SELECT 1 FROM spam_eggs WHERE foo_bar_id = 4 FOR UPDATE;
  2. 2番目の(新しい)セッションで、DELETE FROM foo_bar WHERE id = 4;

2番目のステートメントが最初のステートメントでブロックされていることがわかります。これは、DELETEonが外部キー参照にfoo_barカスケードしspam_eggsて行をロックしようとし、それを削除できるようにするためです。そのロックは、によって保持されているロックをブロックしますSELECT ... FOR SHARE

一般に、これらすべての状況でテストしてみてください。

  • txはBEGIN ISOLATION LEVEL READ COMMITTED、最初に発行されます。ROLLBACK
  • txはBEGIN ISOLATION LEVEL READ COMMITTED、最初に発行されます。COMMIT
  • txはBEGIN ISOLATION LEVEL SERIALIZABLE、最初に発行されます。ROLLBACK
  • txはBEGIN ISOLATION LEVEL SERIALIZABLE、最初に発行されます。COMMIT

あなたが何を期待するかを知っていることを確認するために。それをテストする前にあなたが起こると予想することを通して推論するならば、それはあなたの学習にも良いです。

この場合、READ COMMITTEDSERIALIZABLE分離レベルは同じように動作します。ただし、実際にUPDATEアフターを実行するSELECT ... FOR UPDATECOMMIT、動作が異なります。READ COMMITTEDバージョンはDELETE正常に実行されますが、バージョンSERIALIZABLEは次のように失敗します。

regress=# BEGIN ISOLATION LEVEL SERIALIZABLE;
regress=# DELETE FROM foo_bar WHERE id = 4;                                                                                                                                                                                                    
ERROR:  could not serialize access due to concurrent update                                                                                                                                                        
CONTEXT:  SQL statement "DELETE FROM ONLY "public"."spam_eggs" WHERE $1 OPERATOR(pg_catalog.=) "foo_bar_id""
于 2012-08-20T03:19:12.557 に答える