„Поръчката“ е детерминистична от ваша гледна точка само ако включите ORDER BY в заявката си. Независимо дали е детерминистично от гледна точка на сървъра е детайл за изпълнение, на който не трябва да се разчита.
Що се отнася до заключването, две идентични DML оператора могат да блокират (но не и да блокират) един друг. Например:
CREATE TABLE THE_TABLE (
ID INT PRIMARY KEY
);
Транзакция A:
INSERT INTO THE_TABLE VALUES(1);
Транзакция Б:
INSERT INTO THE_TABLE VALUES(1);
В този момент Транзакция Б е в застой докато транзакция А не се ангажира или се върне назад. Ако A извърши, B се провали поради нарушение на PRIMARY KEY. Ако A се върне назад, B успява.
Подобни примери могат да бъдат конструирани за UPDATE и DELETE.
Важният момент е, че блокирането няма да зависи от плана за изпълнение - без значение как Oracle избере да оптимизира заявката ви, винаги ще имате същото поведение на блокиране. Може да искате да прочетете за автоматичните заключвания в DML операции за повече информация.
Колкото домъртвата -locks, те са възможни за постигане с множество изрази. Например:
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
Или, вероятно с изявления, които променят повече от един ред в различен ред и някои много лоши моменти (може ли някой да потвърди това?).
--- АКТУАЛИЗИРАНЕ ---
В отговор на актуализацията на вашия въпрос, позволете ми да направя едно общо наблюдение:Ако едновременни нишки на изпълнение заключват обекти в последователния ред , задънените места са невъзможни. Това е вярно за всякакъв вид заключване, независимо дали става дума за мютексове във вашата средна многонишкова програма (например вижте мислите на Хърб Сътър относно йерархиите на заключване) или това са бази данни. След като промените реда по такъв начин, че всички две ключалки да бъдат „обърнати“, се въвежда потенциалът за блокиране.
Без да сканирате индекса, вие актуализирате (и заключвате ) редове в един ред и с индекса в друг. Така че вероятно това се случва във вашия случай:
- Ако деактивирате сканирането на индекса и за двете едновременни транзакции , и двата заключват редовете в един и същ ред [X], така че не е възможна застой.
- Ако активирате индексно сканиране само за една транзакция , те вече не заключват редовете в същия ред, оттук и възможността за блокиране.
- Ако активирате сканирането на индекса и за двете транзакции , тогава отново и двете заключват редове в един и същи ред и блокирането е невъзможно (продължете и опитайте
alter session set optimizer_index_cost_adj = 1;
). и в двете сесии и ще видите).
[X] Въпреки че не бих разчитал на пълно сканиране на таблици с гарантиран ред – може просто да е начина, по който работи текущият Oracle при тези специфични обстоятелства, а някои бъдещи Oracle или различни обстоятелства могат да доведат до различно поведение.
Така че наличието на индекс е случайно - истинският проблем е подреждането. Случва се така, че подреждането в UPDATE може да бъде повлияно от индекс, но ако можехме да повлияем на подреждането по друг начин, ще получим подобни резултати.
Тъй като UPDATE няма ORDER BY, не можете наистина да гарантирате реда на заключване само чрез UPDATE. Ако обаче се разделите заключване от актуализиране, тогава можете гарантиране на реда за заключване:
SELECT ... ORDER BY ... FOR UPDATE;
Въпреки че оригиналният ви код причини блокиране в моята среда на Oracle 10, следният код не го прави:
Сесия 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;
/