Mais tarde, tenho enfrentado muitas disputas de bloqueio de linha. A tabela em disputa parece ser uma tabela específica.
Isso geralmente é o que acontece -
- Desenvolvedor 1 inicia uma transação na tela inicial do Oracle Forms
- Desenvolvedor 2 inicia outra transação, de uma sessão diferente usando a mesma tela
~ 5 minutos depois, o front-end parece não responder. A verificação das sessões mostra a contenção de bloqueio de linha. A "solução" que todo mundo joga é 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 tais problemas que codificam?
Se esta pergunta parecer muito aberta/insuficiente, sinta-se à vontade para editar/informar-me - farei o possível para adicionar algumas informações adicionais.
A tabela em questão está sob muitos inserts e atualizações, eu diria que é uma das tabelas mais ocupadas. O SP é bastante complexo - para simplificar - ele busca dados de várias tabelas, popula em tabelas de trabalho, muitas operações aritméticas ocorrem na mesa de trabalho e o resultado da mesa de trabalho é inserido/atualizado na tabela em questão.
A versão do banco de dados é Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64 bits. O fluxo da lógica é executado na mesma ordem em ambas as sessões, a transação não é mantida aberta por muito tempo (ou pelo menos eu acho ) 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, em 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 para esta tabela não estão utilizando o índice. Por que é assim - não tenho certeza. A coluna referenciada na cláusula where é indexada. No momento, estou reconstruindo o index.
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 do Oracle Concepts Guide sobre bloqueios diz: "Uma linha é bloqueada apenas quando modificada por um gravador". Outra sessão atualizando a mesma linha aguardará a primeira sessão
COMMIT
ouROLLBACK
antes de poder 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. TodosCOMMIT
os lançamentos são bloqueados, portanto, se você puder fazer as atualizações em lotes, a probabilidade de outra sessão precisar da mesma linha será 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 e-mail após cada atualização, considere enfileirar os e-mails 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.Vou fornecer uma resposta do ponto de vista do desenvolvedor.
Na minha opinião, quando você encontra uma disputa de linha como a que você descreveu, é porque você tem um bug em 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, entretanto, é que a sessão 2 atualiza cegamente o registro. Suponha que ambas as sessões emitam a declaração:
Após ambas as instruções, as modificações da sessão1 foram sobrescritas, 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 contenção) nunca deve acontecer, eles são 100% evitáveis. 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 facilmente traduzida para o usuário final (essa 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 baseado 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 foi alterada quando sua atualização for emitida. Você pode armazenar em cache o valor de todas as colunas ou pode usar uma coluna de carimbo de data/hora que é atualizada automaticamente ou uma coluna baseada em sequência. Seja qual for a sua escolha, quando você estiver prestes a realizar sua atualização, você se certificará de que 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 confiança total em todos os aplicativos que acessam seu banco de dados, poderá contar com 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 será bloqueado por outra pessoa e evitará a atualização cega. No entanto, se você não bloquear a linha de antemão, 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, independentemente da escolha do esquema de bloqueio (o impacto no desempenho pode ser insignificante, pois você recupera todos os valores, incluindo o rowid, ao bloquear a linha).
TL;DR
Esta resposta provavelmente se qualificaria para uma entrada no The Daily WTF.
Certo, depois de rastrear as sessões e pesquisar
USER_SOURCE
- localizei a causa raiz