web-dev-qa-db-ja.com

UPDATE実行時のPostgreSQLのデッドロック

PostgreSQLのデッドロックについて読んで少し混乱しています。

典型的なデッドロックの例は次のとおりです。

-- Transaction 1
UPDATE customer SET ... WHERE id = 1
UPDATE customer SET ... WHERE id = 2

-- Transaction 2
UPDATE customer SET ... WHERE id = 2
UPDATE customer SET ... WHERE id = 1

しかし、次のようにコードを変更するとどうなりますか?

-- Transaction 1
UPDATE customer SET ... WHERE id IN (1, 2)

-- Transaction 2
UPDATE customer SET ... WHERE id IN (1, 2)

ここでデッドロックが発生する可能性はありますか?

基本的に私の質問は次のとおりです。2番目のケースでは、PostgreSQLは行を1つずつロックしますか、それともWHERE条件でカバーされるスコープ全体をロックしますか?

前もって感謝します!

29
vyakhir

PostgreSQLでは、行は更新されるとロックされます。実際、これが実際に機能する方法は、各タプル(行のバージョン)にxminというシステムフィールドがあり、どのトランザクションがそのタプルを現在の状態にしたかを示します。 (挿入または更新による)およびxmaxと呼ばれるシステムフィールドは、そのタプルが(更新または削除によって)期限切れになったトランザクションを示します。データにアクセスすると、アクティブな「スナップショット」をこれらの値と照合することにより、各タプルをチェックして、トランザクションに表示されているかどうかを判断します。

UPDATEを実行していて、検索条件に一致するタプルに、スナップショットに表示されるxminとアクティブなトランザクションのxmaxがある場合、そのトランザクションが完了するのを待ってブロックします。タプルを最初に更新したトランザクションがロールバックすると、トランザクションがウェイクアップして行を処理します。最初のトランザクションがコミットされると、トランザクションはウェイクアップし、現在のトランザクション分離レベルに応じてアクションを実行します。

明らかに、デッドロックは、これが異なる順序で行に発生した結果です。 RAMには、すべての行に対して同時に取得できる行レベルのロックはありませんが、行が同じ順序で更新されると、循環ロックを使用できません。残念ながら、提案されたIN(1, 2)構文は、それを保証するものではありません。セッションが異なれば、異なるコスト要因がアクティブになる場合があります。バックグラウンドの「分析」タスクによって、あるプランの生成と別のプランの生成の間でテーブルの統計が変更される場合があります。 seqscanを使用していて、PostgreSQLの最適化の影響を受けているため、新しいseqscanがすでに進行中のシーケンスに参加し、「ループアラウンド」してディスクI/Oを削減します。

アプリケーションコードまたはカーソルを使用して、同じ順序で一度に1つずつ更新を行うと、デッドロックではなく、単純なブロックのみが発生します。ただし、一般に、リレーショナルデータベースはシリアル化に失敗する傾向があるため、SQLSTATEに基づいてリレーショナルデータベースを認識し、トランザクション全体を最初から自動的に再試行するフレームワークを介してアクセスするのが最善です。 PostgreSQLでは、シリアル化の失敗は常に40001または40P01のSQLSTATEになります。

http://www.postgresql.org/docs/current/interactive/mvcc-intro.html

37
kgrittn