postgres でのロックのメカニズムをよりよく理解したいと思います。
ツリーがリンゴを持つことができるとしましょう(リンゴテーブルの外部キーを介して)。更新ロックのためにツリーを選択すると、リンゴが取得されるようです。ただし、他の誰かが既にこのリンゴをロックしている場合でも、操作はブロックされません。
なぜそうなのですか?
ps 「select for update」を削除するよう提案しないでください。
シナリオ
Transaction 1 Transaction 2
BEGIN .
update apple; .
. BEGIN
. select tree for update;
. update apple;
. --halts because of the other transaction locking an apple
update apple; .
-- deadlock .
COMMIT
--transaction succeeds
コード
postgres で試してみたい場合は、コピー/貼り付けできるコードを次に示します。
次のデータベーススキーマがあります
CREATE TABLE trees (
id integer primary key
);
create table apples (
id integer primary key,
tree_id integer references trees(id)
);
と非常に単純なデータ
insert into trees values(1);
insert into apples values(1,1);
2 つの単純なトランザクションがあります。1 つはリンゴの更新、2 番目はツリーのロックとリンゴの更新です。
BEGIN;
UPDATE apples SET id = id WHERE id = 1;
-- run second transaction in paralell
UPDATE apples SET id = id WHERE id = 1;
COMMIT;
BEGIN;
SELECT id FROM trees WHERE id = 1 FOR UPDATE;
UPDATE apples SET id = id WHERE id = 1;
COMMIT;
それらを実行すると、最初のトランザクションの 2 回目の更新でデッドロックが発生します。
ERROR: deadlock detected
DETAIL: Process 81122 waits for ShareLock on transaction 227154; blocked by process 81100.
Process 81100 waits for ShareLock on transaction 227153; blocked by process 81122.
CONTEXT: SQL statement "SELECT 1 FROM ONLY "public"."trees" x WHERE "id" OPERATOR(pg_catalog.=) $1 FOR SHARE OF x"