「順序」は、あなたの観点から決定論的です。 クエリにORDERBYを含める場合のみ。 サーバーの観点から決定論的かどうか は実装の詳細であり、信頼できるものではありません。
ロックに関しては、2つの同一のDMLステートメントが互いにブロックできます(デッドロックはできません)。例:
CREATE TABLE THE_TABLE (
ID INT PRIMARY KEY
);
トランザクションA:
INSERT INTO THE_TABLE VALUES(1);
トランザクションB:
INSERT INTO THE_TABLE VALUES(1);
この時点で、トランザクションBは停止されています。 トランザクションAがコミットまたはロールバックするまで。 Aがコミットした場合、PRIMARYKEY違反のためにBは失敗します。 Aがロールバックすると、Bは成功します。
UPDATEとDELETEについても同様の例を作成できます。
重要な点は、ブロッキングは実行プランに依存しないということです。Oracleがクエリの最適化をどのように選択しても、常に同じブロッキング動作が得られます。詳細については、DML操作の自動ロックについてお読みください。
死んだは -ロック、複数のステートメントで実現できます。例:
A: INSERT INTO THE_TABLE VALUES(1);
B: INSERT INTO THE_TABLE VALUES(2);
A: INSERT INTO THE_TABLE VALUES(2);
B: INSERT INTO THE_TABLE VALUES(1); -- SQL Error: ORA-00060: deadlock detected while waiting for resource
または、複数の行を異なる順序で変更するステートメントと、非常に不運なタイミングが含まれている可能性があります(誰かがこれを確認できますか?)。
---更新---
あなたの質問の更新に応えて、私に一般的な観察をさせてください:実行の同時スレッドが一貫した順序でオブジェクトをロックする場合 、デッドロックは不可能です。これは、平均的なマルチスレッドプログラムのミューテックス(たとえば、ロック階層に関するHerb Sutterの考えを参照)であれ、データベースであれ、あらゆる種類のロックに当てはまります。 2つのロックが「反転」するように順序を変更すると、デッドロックが発生する可能性があります。
インデックスをスキャンせずに、更新(およびロック)しています )行を1つの順序で、インデックスを別の順序で使用します。だから、これはおそらくあなたの場合に起こることです:
- 両方の同時トランザクションのインデックススキャンを無効にする場合 、両方とも同じ順序で行をロックする[X]ので、デッドロックは発生しません。
- 1つのトランザクションに対してのみインデックススキャンを有効にする場合 、同じ順序で行をロックしなくなったため、デッドロックが発生する可能性があります。
- 両方のトランザクションのインデックススキャンを有効にする場合 、次に、両方が同じ順序で行をロックしており、デッドロックは不可能です(
alter session set optimizer_index_cost_adj = 1;
を試してください。 両方のセッションで表示されます)。
[X]順序が保証された全表スキャンには依存しませんが、これらの特定の状況での現在のOracleの動作と、将来のOracleまたはさまざまな状況での動作が異なる可能性があります。
したがって、インデックスの存在は偶発的です-本当の問題は順序付けです。 UPDATEでの順序付けはインデックスの影響を受ける可能性がありますが、別の方法で順序付けに影響を与えることができれば、同様の結果が得られます。
UPDATEにはORDERBYがないため、UPDATEだけでロックする順序を実際に保証することはできません。ただし、分離する 更新をロックすると、できます ロックの順序を保証します:
SELECT ... ORDER BY ... FOR UPDATE;
元のコードが私のOracle10環境でデッドロックを引き起こしましたが、次のコードはそうではありません:
セッション1:
declare
cursor cur is select * from deadlock_test where a > 0 order by a 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;
/
セッション2:
alter session set optimizer_index_cost_adj = 1;
declare
cursor cur is select * from deadlock_test where a > 0 order by a 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;
/