A "ordem" é determinística da sua perspectiva somente se você incluir ORDER BY em sua consulta. Se é determinístico da perspectiva do servidor é um detalhe de implementação, não confiável.
Quanto ao bloqueio, duas instruções DML idênticas podem bloquear (mas não travar) uma à outra. Por exemplo:
CREATE TABLE THE_TABLE (
ID INT PRIMARY KEY
);
Transação A:
INSERT INTO THE_TABLE VALUES(1);
Transação B:
INSERT INTO THE_TABLE VALUES(1);
Neste ponto, a Transação B está parada até que a transação A seja confirmada ou revertida. Se A cometer, o B falha devido à violação da CHAVE PRIMÁRIA. Se A reverter, o B é bem-sucedido.
Exemplos semelhantes podem ser construídos para UPDATE e DELETE.
O ponto importante é que o bloqueio não dependerá do plano de execução - não importa como a Oracle escolha otimizar sua consulta, você sempre terá o mesmo comportamento de bloqueio. Você pode querer ler sobre bloqueios automáticos em operações DML para obter mais informações.
Quanto a mortos -locks, eles são possíveis de alcançar com várias instruções. Por exemplo:
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
Ou, possivelmente, com declarações que modificam mais de uma linha em ordem diferente e algum tempo muito azarado (alguém poderia confirmar isso?).
--- ATUALIZAÇÃO ---
Em resposta à atualização de sua pergunta, deixe-me fazer uma observação geral:se threads simultâneos de execução bloquearem objetos na ordem consistente , os impasses são impossíveis. Isso é verdade para qualquer tipo de bloqueio, seja mutexes em seu programa multi-thread médio (por exemplo, veja os pensamentos de Herb Sutter sobre Hierarquias de bloqueio) ou bancos de dados. Depois de alterar a ordem de forma que quaisquer dois bloqueios sejam "invertidos", o potencial de bloqueios é introduzido.
Sem verificar o índice, você está atualizando (e bloqueando ) linhas em uma ordem e com o índice em outra. Então, provavelmente é isso que acontece no seu caso:
- Se você desabilitar a verificação de índice para ambas as transações simultâneas , ambos bloqueiam linhas na mesma ordem [X], portanto, nenhum impasse é possível.
- Se você ativar a verificação de índice para apenas uma transação , eles não bloqueiam mais as linhas na mesma ordem, daí a possibilidade de um impasse.
- Se você ativar a verificação de índice para ambas as transações , então, novamente, ambos estão bloqueando linhas na mesma ordem, e um impasse é impossível (vá em frente e tente
alter session set optimizer_index_cost_adj = 1;
em ambas as sessões e você verá).
[X] Embora eu não confie em verificações de tabela completas com uma ordem garantida - pode ser apenas como o Oracle atual nessas circunstâncias específicas funciona, e alguns Oracles futuros ou circunstâncias diferentes podem produzir um comportamento diferente.
Portanto, a presença do índice é incidental - o verdadeiro problema é o pedido. Acontece que a ordenação em UPDATE pode ser influenciada por um índice, mas se pudéssemos influenciar a ordenação de outra maneira, obteríamos resultados semelhantes.
Como UPDATE não possui ORDER BY, você não pode realmente garantir a ordem de bloqueio apenas por UPDATE. No entanto, se você separar bloqueando a atualização, então você pode garantir a ordem de bloqueio:
SELECT ... ORDER BY ... FOR UPDATE;
Embora seu código original tenha causado deadlocks no meu ambiente Oracle 10, o código a seguir não:
Sessão 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;
/
Sessão 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;
/