Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Um deadlock pode ocorrer com o mesmo método de acesso?


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;
/