PostgreSQL 9.6の状況:
SELECT id FROM A FOR UPDATE;
ブロックUPDATE B SET x=y;
Aのロックが解除されるまで。
これは、参照値が変わる可能性があるためと考えられます。最初のステートメントが外部キー制約を削除せずに2番目のステートメントの実行をブロックしないようにするにはどうすればよいですか?
外部キー制約を削除すると、どのような悪いことが予想されますか?この問題が発生する実際のデータベースでは、Aから削除した後にテーブルBに行が残っていても、大きな問題にはなりません。主キーも更新しません。
編集コード付き:
-- Setup
create table a (id int primary key);
create table b (id int primary key references a (id) on update cascade on delete cascade, x varchar);
insert into a values (1), (2);
insert into b values (1, null), (2, null);
-- Left
begin;
select 1 from a for update;
/* Keep the transaction open */
-- Right
begin;
update b set x = 'abc' where id = 1;
update b set x = 'xyz'; /* It blocks here /*
また、主キーを更新することもありません。
その場合、FOR NO KEY UPDATE
の代わりにFOR UPDATE
を使用できると思います。 Explicit Locking に関するPostgresのドキュメントで説明されているように、これは弱いロックです。
FOR NO KEY UPDATE
FOR UPDATE
と同様に動作しますが、取得されたロックがより弱い点が異なります。このロックは、取得しようとするSELECT FOR KEY SHARE
コマンドをブロックしません。同じ行のロック。このロックモードは、FOR UPDATE
ロックを取得しないUPDATE
によっても取得されます。
テスト:
-- Setup
create table a (id int primary key,
x varchar);
create table b (id int primary key
references a (id) on update cascade on delete cascade,
x varchar);
insert into a values (1), (2);
insert into b values (1, null), (2, null);
-- Left
begin;
select 1 from a for no key update;
/* Keep the transaction open */
-- Right
begin;
update b set x = 'abc' where id = 1;
update b set x = 'xyz'; -- doesn't block
-- Left
update a set x = 'left' where id = 1;
commit ;
-- Right
commit ;
FOR UPDATEを使用すると、SELECTステートメントによって取得された行が、更新の場合と同様にロックされます。これにより、現在のトランザクションが終了するまで、他のトランザクションによってロック、変更、または削除されるのを防ぎます。つまり、これらの行のUPDATEを試みる他のトランザクションは、現在のトランザクションが終了するまでブロックされます。
SOは、FOR UPDATEを削除してロックを解除するだけです。最初のステートメントで "FOR UPDATE"が必要なのはなぜですか。