実行するとしましょう...
SELECT * FROM MY_TABLE FOR UPDATE
...そしてMY_TABLEに複数の行があります。
理論的には、2つの同時トランザクションがこのステートメントを実行しても、行を異なる順序でトラバース(したがってロック)すると、デッドロックが発生する可能性があります。例えば:
- トランザクション1:行Aをロックします。
- トランザクション2:行Bをロックします。
- トランザクション1:行Bとブロックをロックしようとします。
- トランザクション2:行Aとデッドロックをロックしようとします。
これを解決する方法は、ORDER BYを使用して、行が常に同じ順序でロックされるようにすることです。
ですから、私の質問は、この理論上のデッドロックが実際に発生することはあるのでしょうか。人工的に誘発する方法があることは知っていますが、通常の操作で発生する可能性はありますか?常にORDERBYを使用する必要がありますか、それとも実際には省略しても安全ですか?
私は主にOracleとMySQL/InnoDBの動作に興味がありますが、他のDBMSに関するコメントも役に立ちます。
- - 編集 - -
ロック順序が同じでない場合にOracleでデッドロックを再現する方法は次のとおりです。
テストテーブルを作成し、いくつかのテストデータを入力します...
CREATE TABLE DEADLOCK_TEST (
ID INT PRIMARY KEY,
A INT
);
INSERT INTO DEADLOCK_TEST SELECT LEVEL, 1 FROM DUAL CONNECT BY LEVEL <= 10000;
COMMIT;
... 1つのクライアントセッション(SQL Developerを使用)から、次のブロックを実行します。
DECLARE
CURSOR CUR IS
SELECT * FROM DEADLOCK_TEST
WHERE ID BETWEEN 1000 AND 2000
ORDER BY ID
FOR UPDATE;
BEGIN
WHILE TRUE LOOP
FOR LOCKED_ROW IN CUR LOOP
UPDATE DEADLOCK_TEST
SET A = -99999999999999999999
WHERE CURRENT OF CUR;
END LOOP;
ROLLBACK;
END LOOP;
END;
/
別のクライアントセッション(SQL Developerのインスタンスをもう1つ起動しただけです)から、同じブロックを実行しますが、を使用しDESC
ますORDER BY
。数秒後、次の情報が表示されます。
ORA-00060: deadlock detected while waiting for resource
ところで、ORDER BY
(両方のブロックが同一になるように)完全に削除し、...を追加することで、同じ結果が得られる可能性があります。
ALTER SESSION SET OPTIMIZER_INDEX_COST_ADJ = 1;
...1つのブロックの前ですが...
ALTER SESSION SET OPTIMIZER_INDEX_COST_ADJ = 10000;
...他の前に(したがって、Oracleは異なる実行プランを選択し、行を異なる順序でフェッチする可能性があります)。
これは、行がカーソルからフェッチされるときにロックが実際に行われることを示しています(カーソルが開いたときに結果セット全体に対して一度に行われるわけではありません)。