„Porządek” jest deterministyczny z Twojej perspektywy tylko jeśli w zapytaniu uwzględnisz ORDER BY. Czy jest to deterministyczne z perspektywy serwera jest szczegółem implementacji, na którym nie można polegać.
Jeśli chodzi o blokowanie, dwie identyczne instrukcje DML mogą się wzajemnie blokować (ale nie zakleszczać). Na przykład:
CREATE TABLE THE_TABLE (
ID INT PRIMARY KEY
);
Transakcja A:
INSERT INTO THE_TABLE VALUES(1);
Transakcja B:
INSERT INTO THE_TABLE VALUES(1);
W tym momencie transakcja B jest zawieszona dopóki Transakcja A nie zostanie zatwierdzona lub wycofana. Jeśli A się zatwierdzi, B nie powiedzie się z powodu naruszenia KLUCZA PODSTAWOWEGO. Jeśli A wycofuje się, B wygrywa.
Podobne przykłady można skonstruować dla UPDATE i DELETE.
Ważną kwestią jest to, że blokowanie nie będzie zależeć od planu wykonania — bez względu na to, jak Oracle zdecyduje się zoptymalizować zapytanie, zachowanie blokowania będzie zawsze takie samo. Możesz przeczytać o automatycznych blokadach w operacjach DML, aby uzyskać więcej informacji.
Co do martwych -locks, można je osiągnąć za pomocą wielu instrukcji. Na przykład:
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
Lub ewentualnie z oświadczeniami, które modyfikują więcej niż jeden wiersz w innej kolejności i bardzo niefortunnym czasem (czy ktoś mógłby to potwierdzić?).
--- AKTUALIZACJA ---
W odpowiedzi na aktualizację Twojego pytania, pozwólcie, że poczynię ogólną obserwację:jeśli współbieżne wątki wykonania blokują obiekty w spójnej kolejności , impasy są niemożliwe. Dotyczy to każdego rodzaju blokowania, czy to muteksów w przeciętnym programie wielowątkowym (np. zobacz przemyślenia Herba Suttera na temat hierarchii blokad), czy też baz danych. Po zmianie kolejności w taki sposób, że dowolne dwie blokady są „odwrócone”, pojawia się ryzyko zakleszczenia.
Bez skanowania indeksu aktualizujesz (i blokujesz ) wiersze w jednej kolejności, a z indeksem w innej. Tak więc prawdopodobnie dzieje się w twoim przypadku:
- Jeśli wyłączysz skanowanie indeksu dla obu jednoczesnych transakcji , oba blokują wiersze w tej samej kolejności [X], więc zakleszczenie nie jest możliwe.
- Jeśli włączysz skanowanie indeksu tylko dla jednej transakcji , nie blokują już wierszy w tej samej kolejności, stąd możliwość wystąpienia impasu.
- Jeśli włączysz skanowanie indeksu dla obu transakcji , to znowu oba blokują wiersze w tej samej kolejności, a zakleszczenie jest niemożliwe (spróbuj
alter session set optimizer_index_cost_adj = 1;
w obu sesjach i zobaczysz).
[X] Chociaż nie polegałbym na pełnym skanowaniu tabel z gwarantowaną kolejnością — może to być po prostu sposób działania obecnej Oracle w tych szczególnych okolicznościach, a niektóre przyszłe Oracle lub inne okoliczności mogą powodować inne zachowanie.
Tak więc obecność indeksu jest przypadkowa - prawdziwym problemem jest uporządkowanie. Tak się składa, że na zamawianie w UPDATE może mieć wpływ indeks, ale gdybyśmy mogli wpłynąć na zamawianie w inny sposób, otrzymalibyśmy podobne wyniki.
Ponieważ UPDATE nie ma ORDER BY, tak naprawdę nie możesz zagwarantować kolejności blokowania przez samą UPDATE. Jeśli jednak oddzielisz się blokowanie aktualizacji, wtedy możesz gwarantuj kolejność zamka:
SELECT ... ORDER BY ... FOR UPDATE;
Podczas gdy oryginalny kod powodował zakleszczenia w moim środowisku Oracle 10, następujący kod nie:
Sesja 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;
/
Sesja 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;
/