【PostgreSQL】デッドロック発生時の挙動の確認

準備:テーブル作成

PostgreSQLでデッドロックが発生した際の挙動を確認するための準備として以下のようなテーブルを作成しました。

idnameprice
1product1100
2product2200
productテーブル

テーブル作成とデータ投入用SQLは以下の通りです。

-- テーブル作成
create table product(id int, name text, price int);

-- データ投入
insert into product(id, int, name, price) values (1, 'product1', 100),(2, 'product2',200);

UPDATE文によるデッドロック発生時の挙動の確認

ターミナルを2つ起動し、それぞれのターミナルからpsqlでPostgreSQLへ接続しトランザクションを開始します。ここでは説明のため、2つのトランザクションをトランザクションA、トランザクションBとします。

以下のような順でトランザクションA, Bからproduct1, product2に対してUPDATE文を発行すると2つのトランザクションで互いにロック待ちとなるデッドロックが発生します。

-- トランザクションA開始
begin;

-- トランザクションB開始
begin;

-- トランザクションAでproduct1を更新
update product set price=101 where id =1;

-- トランザクションBでproduct2を更新
update product set price=201 where id =2;

-- トランザクションAでproduct2を更新
-- トランザクションBがproduct2をロックしているためロック待ちとなる
update product set price=101 where id =2;

-- トランザクションBでproduct1を更新
-- トランザクションAがproduct1をロックしているためロック待ちとなる
update product set price=201 where id =1;

-- デッドロック発生
ERROR:  deadlock detected
DETAIL:  Process 45 waits for ShareLock on transaction 531; blocked by process 36.
Process 36 waits for ShareLock on transaction 532; blocked by process 45.
HINT:  See server log for query details.
CONTEXT:  while updating tuple (0,1) in relation "product"

-- トランザクションBが中断され、トランザクションAでのproduct2に対するUPDATE文の実行が完了する
-- トランザクションA, Bのどちらが中断させるかは分からない

PostgreSQLではデッドロック発生が検知されるとデッドロックに関連しているトランザクションのどちらか一方が中断されます。このとき、どのトランザクションが中断されるかは正確にはわかりません。この例ではトランザクションA,Bのどちらが中断させるかは予測できません。

PostgreSQL 12.4文章 13.3. 明示的ロック

deadlock_timeoutパラメータによるロック待ち時間の設定

デッドロックと判断するまでにどのくらいの時間ロック待ちするかをdeadlock_timeoutパラメータにより設定できます。デフォルト値は1秒です。

以下のように、setコマンドにより値を変更することができます。

-- deadlock_timeoutパラメータの値を確認
show deadlock_timeout;
 deadlock_timeout
------------------
 1s
(1 row)

-- deadlock_timeoutパラメータの値を10秒(10,000ms)に設定
set deadlock_timeout = 10000;

PostgreSQL 12.4文章 19.12. ロック管理