1

Postgres データベースに原因不明のデッドロックが発生しています。関連するクエリを単純化すると、デッドロックに関係するトランザクションの 1 つが次のようになります。

BEGIN;
UPDATE A SET CHUNK_ID=1, STATUS='PROCESSING' WHERE ID IN (
    SELECT ID FROM A
    WHERE CHUNK_ID IS NULL
    ORDER BY O_ID
    LIMIT 1000
    FOR UPDATE 
);
COMMIT;

もう1つは次のとおりです。

BEGIN;
UPDATE A SET STATUS='SENT' WHERE ID = 1; 
UPDATE A SET STATUS='SENT' WHERE ID = 2;
UPDATE A SET STATUS='SENT' WHERE ID = 3;
...
COMMIT; 

私の質問は、ここでデッドロックが発生する可能性があるのはなぜですか? 同時に実行されている他のクエリに関係なく、最初のトランザクションがデッドロックになるシナリオは考えられません。

そのようなケースはありますか?つまり、ネストされた SELECT ... FOR UPDATE を使用する UPDATE はデッドロックの一部になる可能性がありますか?

ありがとう

4

1 に答える 1

0

(これは推測ですが、うまくいけば知識のあるものです。)

すべては、SELECT ... ORDER BY O_ID ... FOR UPDATE によって行がロックされる順序に依存します。O_ID の順序が ID の順序と異なる場合、次のような状況が完全に発生する可能性があります。

ID    O_ID
--    ----
1     2
2     1
  • トランザクション A は ID=2 の行をロックします。
  • トランザクション B は ID=1 の行をロックします。
  • トランザクション A は ID=1 の行をロックしようとしますが、トランザクション B を待たなければなりません。
  • トランザクション B は ID=2 の行をロックしようとしますが、トランザクション A を待たなければなりません。

警告: O_ID の順序が ID の順序と同じであっても、ORDER BY 句が実際にはロックの順序を保証していない可能性があります (結果が返される順序を保証しているだけです)。残念ながら、これは十分に文書化されていないようです。価値があるのは、Oracleがロックに関しては(常に)ORDER BYを尊重しないように見えるため、PostgreSQLの下でも注意することです。

一般に、デッドロックの解決策は、常に同じ順序でロックすることです。ORDER BY が実際にロックの順序を保証すると仮定すると、単純に SELECT ... ORDER BY O_ID ... FOR UPDATE を 2 番目のトランザクションに含めることができます。または、最初のトランザクションで ORDER BY ID を使用します。

ところで、そもそもなぜ明示的にロックしているのですか? あなたはそれで何を達成しようとしていますか?

于 2013-02-19T19:00:16.397 に答える