"Rækkefølgen" er deterministisk fra dit perspektiv kun hvis du inkluderer ORDER BY i din forespørgsel. Om det er deterministisk fra serverens perspektiv er en implementeringsdetalje, der ikke skal stoles på.
Hvad angår låsning, kan to identiske DML-sætninger blokere (men ikke deadlock) hinanden. For eksempel:
CREATE TABLE THE_TABLE (
ID INT PRIMARY KEY
);
Transaktion A:
INSERT INTO THE_TABLE VALUES(1);
Transaktion B:
INSERT INTO THE_TABLE VALUES(1);
På dette tidspunkt er Transaktion B stoppet indtil Transaktion A enten forpligter eller ruller tilbage. Hvis A begår, fejler B på grund af PRIMÆR NØGLE-overtrædelse. Hvis A ruller tilbage, lykkes B.
Lignende eksempler kan konstrueres til UPDATE og DELETE.
Det vigtige er, at blokering ikke vil afhænge af eksekveringsplanen - uanset hvordan Oracle vælger at optimere din forespørgsel, vil du altid have den samme blokeringsadfærd. Du vil måske læse om automatiske låse i DML Operations for mere info.
Hvad angår døde -låse, de er mulige at opnå med flere udsagn. For eksempel:
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
Eller muligvis med udsagn, der ændrer mere end én række i forskellig rækkefølge og en meget uheldig timing (kan nogen bekræfte dette?).
--- OPDATERING ---
Som svar på opdatering af dit spørgsmål, lad mig komme med en generel observation:Hvis samtidige tråde af udførelse låser objekter i den konsistente rækkefølge , dødvande er umulige. Dette gælder for enhver form for låsning, hvad enten det er mutexes i dit gennemsnitlige multi-threaded program (se f.eks. Herb Sutters tanker om Lock Hierarchies) eller det være sig databaser. Når først du ændrer rækkefølgen på en sådan måde, at to vilkårlige låse "vælges", introduceres potentialet for dødvande.
Uden at scanne indekset, opdaterer du (og låser ) rækker i én rækkefølge og med indekset i en anden. Så dette er sandsynligvis, hvad der sker i dit tilfælde:
- Hvis du deaktiverer indeksscanning for begge samtidige transaktioner , de låser begge rækker i samme rækkefølge [X], så ingen dødvande er mulig.
- Hvis du aktiverer indeksscanning for kun én transaktion , låser de ikke længere rækker i samme rækkefølge, og derfor er der risiko for en dødvande.
- Hvis du aktiverer indeksscanning for begge transaktioner , så igen låser de begge rækker i samme rækkefølge, og en deadlock er umulig (fortsæt og prøv
alter session set optimizer_index_cost_adj = 1;
i begge sessioner, og du vil se).
[X] Selvom jeg ikke ville stole på, at scanninger af hele bord har en garanteret rækkefølge - det kan bare være, hvordan det nuværende Oracle fungerer under disse specifikke omstændigheder, og nogle fremtidige Oracle eller andre omstændigheder kan give en anden adfærd.
Så tilstedeværelsen af indeks er tilfældig - det virkelige problem er bestilling. Det er bare sådan, at bestilling i UPDATE kan påvirkes af et indeks, men hvis vi kunne påvirke bestilling på en anden måde, ville vi få lignende resultater.
Da UPDATE ikke har ORDER BY, kan du ikke rigtig garantere rækkefølgen af låsning ved UPDATE alene. Men hvis du adskiller låser fra at opdatere, så kan du garantere låserækkefølgen:
SELECT ... ORDER BY ... FOR UPDATE;
Mens din originale kode forårsagede dødvande i mit Oracle 10-miljø, gør følgende kode det ikke:
Session 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;
/
Session 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;
/