このようなコードを並行して実行する2つのプロセスがあります。
begin;
update foos set unread=false where owner_id=123 and unread=true;
commit;
これによりデッドロックが発生します。
デッドロックの原因についての私の理解は、 この質問 で説明されているシナリオに似ており、 "織り交ぜられた" UPDATEステートメントが2つの異なる行を異なる順序で更新します。単一のUPDATEステートメントでデッドロックが発生する方法を理解できません。開発環境で2つの並列PSQLセッションを使用してデッドロックシナリオを複製することができません。なぜそれを複製できないのかについての私の推測:
この単一のUPDATEがデッドロックを作成することは可能ですか?
ステートメントはいくつかの行を変更します。これらの各行は、更新時にロックされます。
並行トランザクションのステートメントがこれらの行の1つをすでにロックしているため、UPDATE
がブロックされている可能性があります。その後、並行トランザクションがUPDATE
がすでにロックしている行の1つをロックしようとすると、デッドロックが発生します。
ローレンツは説明しました デッドロックにつながる可能性のあるメカニズムであり、ケビンによるより詳細な説明へのリンクをすでに自分で含めています。
デッドロックを複製する方法をステップごとに説明します。SELECT .. FOR UPDATE
と同じように、プレーンUPDATE
でも機能します。
今、どのように問題を回避する?
実質的なシェアまたはすべてのテーブルを更新する場合-そして、余裕があれば-ただ テーブルを書き込みロックする です。通常、これは進むべき道ではありません。それ以外の場合、3つの異なるアプローチ:
マニュアル はデッドロックの章でこのアドバイスを持っています:
デッドロックに対する最善の防御策は、データベースを使用するすべてのアプリケーションが複数のオブジェクトのロックを一貫した順序で確実に取得することで、デッドロックを回避することです。
UPDATE
のno ORDER BY
がまだ存在する理由がわかりません。しかし、それは私たちが協力しなければならないことです。代わりに、同じトランザクションでSELECT ... FOR UPDATE
を使用して行をロックします- 前の質問 が示すように、すでに試行したように。重要な決定論ORDER BY
を忘れただけです。
BEGIN;
SELECT FROM foos WHERE owner_id = 123 AND unread
ORDER BY ??? -- any deterministic order, PK would be an obvious candidate
FOR UPDATE;
UPDATE foos SET unread = false WHERE owner_id = 123 AND unread;
END;
明らかに、all潜在的に競合するトランザクションは、同じ順序でロックを取得する必要があります。
ロックされていない行のみを処理します。
BEGIN;
SELECT FROM foos WHERE owner_id = 123 AND unread
-- ORDER BY ??? -- optional in this case
FOR UPDATE SKIP LOCKED;
UPDATE foos SET unread = false WHERE owner_id = 123 AND unread;
END;
スキップされた行が、同じことをしている競合するトランザクションによって処理されたことが確実な場合は、ここで完了です。 (本気ですか?)
それ以外の場合は、確認のためにチェックを続けます。
SELECT EXISTS (SELECT FROM foos WHERE owner_id = 123 AND unread);
ライターはリーダーをブロックせず、リーダーはライターをブロックしないため、最後の行がすべて正常に更新されるまでTRUE
を返します。 LoopUPDATE
を取得するまで、上記のFALSE
ブロックの後にこれが(適切な遅延を伴って)続きます。 そして、完了です。
ORDER BY
が大幅なコストを追加する大きなセットの場合は安くなる可能性があります。 OTOH、一致するインデックスがある場合は、ORDER BY
を追加しても意味があります...
上記と同様ですが、一度に更新される行は1つだけです。通常はより高価ですが、正しく行われれば、デッドロックの可能性は排除されます。単一の行の処理にすでに長い時間がかかる場合は、これを考慮してください。
詳細な説明(主に上記にも当てはまります)と手順: