AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 1726
Accepted
Sathyajith Bhat
Sathyajith Bhat
Asked: 2011-03-15 01:11:07 +0800 CST2011-03-15 01:11:07 +0800 CST 2011-03-15 01:11:07 +0800 CST

Rastreando, depurando e corrigindo contenções de bloqueio de linha

  • 772

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.

oracle-10g locked-objects
  • 3 3 respostas
  • 17258 Views

3 respostas

  • Voted
  1. Best Answer
    Leigh Riffel
    2011-03-16T06:21:29+08:002011-03-16T06:21:29+08:00

    Seria possível descobrir qual linha de um procedimento armazenado está causando essas contenções de bloqueio de linha?

    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.

    SELECT sid, sql_text
    FROM v$session s
    LEFT JOIN v$sql q ON q.sql_id=s.sql_id
    WHERE state = 'WAITING' AND wait_class != 'Idle'
    AND event = 'enq: TX - row lock contention';
    

    Qual seria a diretriz geral para reduzir/evitar/eliminar tais problemas com a codificação?

    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 COMMITou ROLLBACKantes 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.

    • COMMITmais frequentemente. Todos COMMITos 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.
    • Certifique-se de não atualizar nenhuma linha sem alterar seus valores. Por exemplo, 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.
    • Certifique-se de usar sequências em vez de bloquear uma tabela para adicionar uma ao valor atual mais alto.
    • Verifique se você não está usando uma função que está fazendo com que um índice não seja usado. Se a função for necessária, considere torná-la um índice baseado em função.
    • Pense em conjuntos. Considere se um loop executando um bloco de PL/SQL fazendo atualizações pode ser reescrito como uma única instrução de atualização. Caso contrário, talvez o processamento em massa possa ser usado com BULK COLLECT ... FORALL.
    • Reduza o trabalho que é feito entre o primeiro UPDATEe o COMMIT. 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.
    • Projete o aplicativo para lidar com a espera fazendo um SELECT ... FOR UPDATE NOWAITou WAIT 2. Você pode detectar a incapacidade de bloquear a linha e informar ao usuário que outra sessão está modificando os mesmos dados.
    • 10
  2. Vincent Malgrat
    2011-03-16T09:02:59+08:002011-03-16T09:02:59+08:00

    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:

    Uma atualização perdida ocorre quando:

    sessão 1: leia o registro do funcionário de Tom

    sessão 2: leia o registro do funcionário de Tom

    sessão 1: atualize o registro do funcionário de Tom

    sessão 2: atualize o registro do funcionário de Tom

    A sessão 2 sobrescreverá as alterações da sessão 1 sem nunca vê-las -- resultando em 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:

    UPDATE table SET col1=:col1, ..., coln=:coln WHERE id = :pk
    

    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 NOWAITinstruçã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:

    SELECT <...>
      FROM table
     WHERE id = :id
       AND marker = :marker
       FOR UPDATE NOWAIT
    

    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:

    UPDATE table
       SET <...>, 
           marker = marker + 1
     WHERE id = :id;
    

    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

    • Atualizar uma linha sem ter um bloqueio prévio expõe o aplicativo a um possível "congelamento". Isso pode ser evitado se todos os DML para o banco de dados implementarem bloqueio otimista ou pessimista.
    • Verifique se a instrução SELECT retorna valores consistentes com qualquer SELECT anterior (para evitar qualquer problema de perda de atualização)
    • 7
  3. Sathyajith Bhat
    2011-03-16T06:42:45+08:002011-03-16T06:42:45+08:00

    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

    • A causa, sem surpresa, foi uma lógica falha
    • Recentemente, uma declaração de atualização foi adicionada ao SP. A instrução de atualização basicamente atualizaria a tabela inteira. Aparentemente, o desenvolvedor em questão se esqueceu de adicionar as cláusulas right where para atualizar as instruções necessárias.
    • A tabela que estava sendo atualizada era como mencionado acima, uma das tabelas mais movimentadas e com um grande número de registros. A atualização levaria um tempo longo e agonizante.
    • O resultado foi que outras sessões não conseguiram obter um bloqueio na mesa e permaneceram em disputas de bloqueio de linha.
    • 5

relate perguntas

  • Obtendo o erro "ORA-00911" quando tento inserir mais de 1 linha

  • Executar bloco de script PL/SQL em C#

  • Como posso remover bloqueios em um objeto?

  • Como obter o documento XML virtual subjacente do Oracle Text Index?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Como você mostra o SQL em execução em um banco de dados Oracle?

    • 2 respostas
  • Marko Smith

    Como selecionar a primeira linha de cada grupo?

    • 6 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Posso ver Consultas Históricas executadas em um banco de dados SQL Server?

    • 6 respostas
  • Marko Smith

    Como uso currval() no PostgreSQL para obter o último id inserido?

    • 10 respostas
  • Marko Smith

    Como executar o psql no Mac OS X?

    • 11 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Marko Smith

    Passando parâmetros de array para um procedimento armazenado

    • 12 respostas
  • Martin Hope
    Manuel Leduc Restrição exclusiva de várias colunas do PostgreSQL e valores NULL 2011-12-28 01:10:21 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Stuart Blackler Quando uma chave primária deve ser declarada sem cluster? 2011-11-11 13:31:59 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    BrunoLM Guid vs INT - Qual é melhor como chave primária? 2011-01-05 23:46:34 +0800 CST
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +0800 CST
  • Martin Hope
    Patrick Como posso otimizar um mysqldump de um banco de dados grande? 2011-01-04 13:13:48 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve