web-dev-qa-db-ja.com

PostgreSQLの他のテーブルをロックするSELECT FOR UPDATE

PostgreSQL 9.6の状況:

  • 整数の主キーを持つテーブルA
  • テーブルAの主キーを参照する主キーに外部キー制約があるテーブルB

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 /*
5
Julien

また、主キーを更新することもありません。

その場合、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 ;
7
ypercubeᵀᴹ

FOR UPDATEを使用すると、SELECTステートメントによって取得された行が、更新の場合と同様にロックされます。これにより、現在のトランザクションが終了するまで、他のトランザクションによってロック、変更、または削除されるのを防ぎます。つまり、これらの行のUPDATEを試みる他のトランザクションは、現在のトランザクションが終了するまでブロックされます。

SOは、FOR UPDATEを削除してロックを解除するだけです。最初のステートメントで "FOR UPDATE"が必要なのはなぜですか。

0
shubham