Até tarde, tenho enfrentado muitas contenções de bloqueio de linha. A tabela em disputa parece ser uma tabela específica.
Geralmente é o que acontece -
- O desenvolvedor 1 inicia uma transação na tela front-end do Oracle Forms
- O desenvolvedor 2 inicia outra transação, a partir de uma sessão diferente, usando a mesma tela
~ 5 minutos depois, o front end parece não responder. A verificação de sessões mostra a contenção de bloqueio de linha. A "solução" que todo mundo usa é matar sessões: /
Como desenvolvedor de banco de dados
- O que pode ser feito para eliminar as contenções de bloqueio de linha?
- Seria possível descobrir qual linha de um procedimento armazenado está causando essas contenções de bloqueio de linha
- Qual seria a diretriz geral para reduzir / evitar / eliminar esses problemas que codificam?
Se essa pergunta parecer muito aberta / insuficiente, fique à vontade para editar / me avisar - farei o possível para adicionar algumas informações adicionais.
A tabela em questão está sujeita a muitas inserções e atualizações, eu diria que é uma das tabelas mais movimentadas. O SP é bastante complexo - para simplificar - busca dados de várias tabelas, preenche-os em tabelas de trabalho, muitas operações aritméticas ocorrem na tabela de trabalho e o resultado da tabela de trabalho é inserido / atualizado na tabela em questão.
A versão do banco de dados é o Oracle Database 10g Enterprise Edition, versão 10.2.0.1.0 - 64 bits. O fluxo da lógica é executado na mesma ordem nas duas sessões, a transação não é mantida aberta por muito tempo (ou pelo menos acho que sim) e os bloqueios ocorrem durante a execução ativa das transações.
Atualização: a contagem de linhas da tabela é maior do que eu esperava, com cerca de 3,1 milhões de linhas. Além disso, depois de rastrear uma sessão, descobri que algumas instruções de atualização nesta tabela não estão utilizando o índice. Por que é assim - não tenho certeza. A coluna referenciada na cláusula where é indexada. Atualmente, estou recriando o índice.
fonte
COMMIT
ouROLLBACK
em um tempo razoável ou b) organizar de modo que as mesmas pessoas nem sempre desejem a mesma linha ao mesmo tempo.Respostas:
Não exatamente, mas você pode obter a instrução SQL que está causando o bloqueio e, por sua vez, identificar as linhas relacionadas no procedimento.
A seção Oracle Concepts Guide sobre bloqueios diz: "Uma linha é bloqueada somente quando modificada por um gravador". Outra sessão que atualiza a mesma linha aguardará a primeira sessão
COMMIT
ouROLLBACK
antes de continuar. Para eliminar o problema, você pode serializar os usuários, mas aqui estão algumas coisas que podem reduzir o problema, talvez ao nível de não ser um problema.COMMIT
mais frequentemente. Todas asCOMMIT
liberações são bloqueadas, portanto, se você pode fazer as atualizações em lotes, a probabilidade de outra sessão precisar da mesma linha é reduzida.UPDATE t1 SET f1=DECODE(f2,’a’,f1+1,f1);
deve ser reescrito como o mais seletivo (leia menos bloqueios)UPDATE t1 SET f1=f1+1 WHERE f2=’a’;
. Obviamente, se a alteração da instrução ainda bloquear a maioria das linhas na tabela, a alteração terá apenas um benefício de legibilidade.BULK COLLECT ... FORALL
.UPDATE
e oCOMMIT
. Por exemplo, se o código enviar um email após cada atualização, considere enfileirar os emails e enviá-los após confirmar as atualizações.SELECT ... FOR UPDATE NOWAIT
ouWAIT 2
. Você pode detectar a incapacidade de bloquear a linha e informar ao usuário que outra sessão está modificando os mesmos dados.fonte
Fornecerei uma resposta do ponto de vista do desenvolvedor.
Na minha opinião, quando você encontra uma disputa de linha como a que você descreve, é porque você tem um bug no seu aplicativo. Na maioria dos casos, esse tipo de contenção é um sinal de vulnerabilidade de atualização perdida. Este tópico no AskTom explica o conceito de uma atualização perdida:
Você experimentou um efeito colateral desagradável de atualização perdida: a sessão 2 pode ser bloqueada porque a sessão 1 ainda não foi confirmada. O principal problema, porém, é que a sessão 2 atualiza cegamente o registro. Suponha que ambas as sessões emitam a declaração:
Após as duas instruções, as modificações da sessão1 foram substituídas, sem que a sessão2 tenha sido notificada de que a linha foi modificada pela sessão 1.
A atualização perdida (e o efeito colateral da disputa) nunca deve acontecer, pois é 100% evitável. Você deve usar o bloqueio para evitá-los com dois métodos principais: bloqueio otimista e pessimista .
1) Bloqueio pessimista
Você deseja atualizar uma linha. Nesse modo, você impedirá que outras pessoas modifiquem essa linha solicitando um bloqueio nessa linha (
SELECT ... FOR UPDATE NOWAIT
instrução). Se a linha já estiver sendo modificada, você receberá uma mensagem de erro, que pode ser traduzida normalmente para o usuário final (esta linha está sendo modificada por outro usuário). Se a linha estiver disponível, faça suas modificações (UPDATE) e confirme sempre que sua transação for concluída.2) Bloqueio otimista
Você deseja atualizar uma linha. No entanto, você não deseja manter um bloqueio nessa linha, talvez porque use várias transações para atualizar a linha (aplicativo sem estado na Web) ou talvez não queira que nenhum usuário mantenha um bloqueio por muito tempo ( o que pode resultar no bloqueio de outras pessoas). Nesse caso, você não solicitará um bloqueio imediatamente. Você usará um marcador para garantir que a linha não seja alterada quando sua atualização for emitida. Você pode armazenar em cache o valor de todas as colunas ou usar uma coluna de carimbo de data / hora atualizada automaticamente ou uma coluna baseada em sequência. Seja qual for a sua escolha, quando você estiver prestes a executar sua atualização, verifique se o marcador nessa linha não foi alterado emitindo uma consulta como:
Se a consulta retornar uma linha, faça sua atualização. Caso contrário, isso significa que alguém modificou a linha desde a última vez que você a consultou. Você terá que reiniciar o processo desde o início.
Nota: Se você tiver uma confiança completa em todos os aplicativos que acessam seu banco de dados, poderá confiar em uma atualização direta para o bloqueio otimista. Você pode emitir diretamente:
Se a instrução não atualizar nenhuma linha, você saberá que alguém alterou essa linha e precisará começar tudo de novo.
Se todos os aplicativos concordarem com esse esquema, você nunca seria bloqueado por outra pessoa e evitaria a atualização cega. No entanto, se você não bloquear a linha antecipadamente, ainda estará suscetível ao bloqueio indefinido se outro aplicativo, trabalho em lote ou atualização direta não implementar o bloqueio otimista. É por isso que aconselho sempre bloquear a linha, qualquer que seja sua opção de esquema de bloqueio (a ocorrência de desempenho pode ser insignificante, pois você recupera todos os valores, incluindo o rowid ao bloquear a linha).
TL; DR
fonte
Essa resposta provavelmente se qualificaria para uma entrada no The Daily WTF.
Certo, depois de rastrear as sessões e pesquisar
USER_SOURCE
- eu rastreei a causa raizfonte