De "volgorde" is bepalend vanuit jouw perspectief alleen als u ORDER BY in uw zoekopdracht opneemt. Of het deterministisch is vanuit het perspectief van de server is een implementatiedetail waarop u niet kunt vertrouwen.
Wat vergrendeling betreft, kunnen twee identieke DML-statements elkaar blokkeren (maar niet deadlocken). Bijvoorbeeld:
CREATE TABLE THE_TABLE (
ID INT PRIMARY KEY
);
Transactie A:
INSERT INTO THE_TABLE VALUES(1);
Transactie B:
INSERT INTO THE_TABLE VALUES(1);
Op dit moment is Transactie B vastgelopen totdat Transactie A zich vastlegt of terugdraait. Als A zich begaat, mislukt de B vanwege schending van de PRIMAIRE SLEUTEL. Als A terugrolt, slaagt de B.
Soortgelijke voorbeelden kunnen worden gemaakt voor UPDATE en DELETE.
Het belangrijke punt is dat blokkeren niet afhankelijk is van het uitvoeringsplan - ongeacht hoe Oracle uw query optimaliseert, u zult altijd hetzelfde blokkeringsgedrag hebben. Misschien wilt u meer lezen over automatische vergrendelingen in DML-bewerkingen voor meer informatie.
Wat betreft dood -locks, ze zijn mogelijk te bereiken met meerdere verklaringen. Bijvoorbeeld:
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
Of mogelijk met uitspraken die meer dan één rij in verschillende volgorde wijzigen en een zeer ongelukkige timing (kan iemand dit bevestigen?).
--- BIJWERKEN ---
In antwoord op de update van uw vraag, wil ik een algemene opmerking maken:als gelijktijdige uitvoeringsthreads objecten vergrendelen in de consistente volgorde , impasses zijn onmogelijk. Dit geldt voor elke vorm van vergrendeling, of het nu gaat om mutexen in uw gemiddelde multi-threaded programma (zie bijvoorbeeld Herb Sutter's gedachten over Lock Hiërarchieën) of het zijn databases. Zodra u de volgorde op zo'n manier wijzigt dat twee sloten worden "omgedraaid", wordt het potentieel voor impasses geïntroduceerd.
Zonder de index te scannen, ben je aan het updaten (en vergrendelen) ) rijen in de ene volgorde en met de index in een andere. Dit is dus waarschijnlijk wat er in jouw geval gebeurt:
- Als u indexscan voor beide gelijktijdige transacties uitschakelt , ze vergrendelen beide rijen in dezelfde volgorde [X], dus er is geen impasse mogelijk.
- Als u indexscan voor slechts één transactie inschakelt , ze vergrendelen rijen niet langer in dezelfde volgorde, vandaar het potentieel voor een impasse.
- Als u indexscan voor beide transacties inschakelt , dan zijn beide weer vergrendelde rijen in dezelfde volgorde, en een impasse is onmogelijk (ga je gang en probeer
alter session set optimizer_index_cost_adj = 1;
in beide sessies en je zult het zien).
[X] Hoewel ik niet zou vertrouwen op volledige tafelscans met een gegarandeerde volgorde, zou het kunnen zijn hoe het huidige Oracle in deze specifieke omstandigheden werkt, en een toekomstig Oracle of andere omstandigheden kunnen ander gedrag veroorzaken.
De aanwezigheid van een index is dus incidenteel - het echte probleem is bestellen. Toevallig kan de volgorde in UPDATE worden beïnvloed door een index, maar als we de volgorde op een andere manier zouden kunnen beïnvloeden, zouden we vergelijkbare resultaten krijgen.
Aangezien UPDATE geen ORDER BY heeft, kunt u de volgorde van vergrendelen niet echt garanderen door UPDATE alleen. Als u echter scheidt blokkeren van updaten, dan kunt u de vergrendelingsvolgorde garanderen:
SELECT ... ORDER BY ... FOR UPDATE;
Terwijl uw originele code impasses veroorzaakte in mijn Oracle 10-omgeving, doet de volgende code dat niet:
Sessie 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;
/
Sessie 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;
/