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 / 323040
Accepted
ChrisJ
ChrisJ
Asked: 2023-02-04 18:08:01 +0800 CST2023-02-04 18:08:01 +0800 CST 2023-02-04 18:08:01 +0800 CST

Evitando deadlocks ao bloquear várias linhas sem usar NOWAIT

  • 772

Em algumas respostas , fui informado de que NOWAITé necessário evitar impasses, o que me surpreendeu porque não é mencionado na documentação do Postgres sobre bloqueios e impasses

Dadas as 3 sessões seguintes:

CREATE TABLE my_table (id int primary key);

-- Session 1
BEGIN;
SELECT id FROM my_table
WHERE id IN (1,2,5)
ORDER BY id
FOR UPDATE

-- Session 2, started after the select in session 1 (before it is committed)
BEGIN
SELECT id FROM my_table
WHERE id IN (4,5,6)
ORDER BY id
FOR UPDATE

-- Session 3, started after the select in session 2 (before 1 or 2 are committed)
BEGIN
SELECT id FROM my_table
WHERE id IN (5,6,7)
ORDER BY id
FOR UPDATE

Eu pensei que cada consulta esperaria uma vez que chegasse a uma linha bloqueada e continuaria bloqueando na ordem especificada por ORDER BY. No entanto, com base nessas respostas, parece que a ordem de bloqueio não é garantida mesmo com um ORDER BYe é possível que essas instruções possam travar?

Espero evitar o uso NOWAITporque o cenário em que estou trabalhando faz parte de uma transação bastante grande e, presumivelmente, o erro resultante invalidaria a transação e eu teria que revertê-la e começar tudo de novo.

As transações são relativamente rápidas, então não me importo de esperar se puder evitar impasses.

Se for verdade que o acima não garante o pedido, a única maneira que vejo de fazer isso seria

SELECT id FROM my_table WHERE id = 1 FOR UPDATE;
SELECT id FROM my_table WHERE id = 2 FOR UPDATE;
...etc

O que parece tedioso. Existe uma maneira melhor?

postgresql
  • 1 1 respostas
  • 57 Views

1 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2023-02-05T09:03:25+08:002023-02-05T09:03:25+08:00

    Como discutimos nesta resposta relacionada , as linhas podem ser bloqueadas fora de ordem se, e somente se ...

    colunas que contribuem para isso ORDER BYpodem ser atualizadas por transações simultâneas

    Isso não deve se aplicar ao seu exemplo com ORDER BY id. Sua coluna "id" parece uma coluna PK substituta que nunca é atualizada em primeiro lugar. Então você está seguro.

    Por que ainda pode haver impasses?

    A defesa recomendada contra impasses é obter bloqueios em uma ordem de classificação consistente e determinística.

    Mas há uma brecha nesse regime confiável: SELECT ... FOR UPDATEbloqueios após a aplicação de filtros ( WHERE) e classificação ( ORDER BY). Se um concorrente UPDATEtiver bloqueado uma ou mais das linhas qualificadas, a primeira consulta deverá aguardar (a menos que seja instruído de outra forma com NOWAIT).
    Se isso UPDATEfor confirmado, a primeira consulta agora prosseguirá e obterá os bloqueios de linha conforme planejado. Mas não classifica as linhas novamente. Se qualquer valor de coluna envolvido em ORDER BYfoi alterado de forma disruptiva, uma linha que anteriormente teria sido bloqueada em ordem, está efetivamente bloqueada fora de ordem agora, reintroduzindo assim a possibilidade de impasses. Normalmente, isso ainda é extremamente improvável de acontecer. Mas alguns padrões de acesso são mais suscetíveis do que outros.

    Considere este caso de teste mínimo. Execute as etapas em três sessões separadas para reproduzir o efeito:

    CREATE TABLE tbl (
      id int PRIMARY KEY
    , sort real NOT NULL
    );
    
    INSERT INTO tbl (id, sort) VALUES (1,1), (2,2), (3,3);
    
    
    -- Session 1
    BEGIN;
    UPDATE tbl
    SET    sort = 4
    WHERE  sort = 2;
    
                   -- Session 2
                   BEGIN;
                   SELECT sort
                   FROM   tbl
                   ORDER  BY sort
                   FOR    UPDATE;
    
                               -- Session 3
                               BEGIN;
                               SELECT sort
                               FROM   tbl
                               WHERE  sort = 3
                               FOR    UPDATE;
                               -- wait
    COMMIT;
    
                               SELECT sort
                               FROM   tbl
                               WHERE  sort = 4
                               FOR    UPDATE;
    

    A sessão 1 atualiza a linha 2, bloqueando-a.
    A sessão 2 tenta bloquear as linhas em ordem, mas, depois de bloquear a linha 1, tem que esperar pela linha 2.
    A sessão 3 bloqueia a linha 3.
    A sessão 1 confirma. Isso aciona a retomada da sessão 2. A linha 2 agora tem sort = 4, mas está bloqueada de qualquer maneira. Em seguida, ele espera pela linha 3, bloqueada pela sessão 3.
    A sessão 3 tenta bloquear a linha 4, mantendo a ordem de classificação necessária para bloqueios, mas agora entra em conflito de qualquer maneira.
    Boom ... ? impasse!

    Eu recebo isso da sessão 3:

    ERROR:  deadlock detected
    DETAIL:  Process 9700 waits for ShareLock on transaction 4412; blocked by process 9663.
    Process 9663 waits for ShareLock on transaction 4413; blocked by process 9700.
    HINT:  See server log for query details.
    CONTEXT:  while locking tuple (0,4) in relation "tbl"
    

    ("Funciona" com apenas duas transações também. É mais fácil realizar com uma terceira.)

    Relacionado:

    • Como simular deadlock no PostgreSQL?

    Mas não podemos prever com segurança se a sessão 2 ou a sessão 3 será inicializada.

    Analise se você pode evitar o problema completamente. Idealmente, você não atualiza as colunas envolvidas na ordem de classificação - ou classifica por colunas que não atualiza (como um PK substituto). Então você está seguro. Caso contrário, você terá que pesar os custos e as consequências. Você fala de uma transação "bastante grande" e de transações "relativamente rápidas"...

    Para a transação "bastante grande", você pode usar um SAVEPOINTem uma transação SQL. Ou use uma EXCEPTIONcláusula em um bloco de código PL/pgSQL. Em seguida, tente obter os bloqueios necessários com NOWAIT, pegue o código SQLSTATE (código de erro) 55P03( lock_not_available), em um loop até obter sucesso. Ou saia NOWAITe pegue 40P01( deadlock_detected). Isso adiciona uma sobrecarga considerável, mas pode valer a pena para proteger uma transação "bastante grande". Geralmente, se ocorrerem impasses com sua carga de trabalho, esteja preparado para repetir as transações que falham.

    • 2

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

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

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

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

    • 4 respostas
  • Marko Smith

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

    • 10 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
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +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

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