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 / 330094
Accepted
Alex F
Alex F
Asked: 2023-08-09 20:34:53 +0800 CST2023-08-09 20:34:53 +0800 CST 2023-08-09 20:34:53 +0800 CST

INSERTs simultâneos que bloqueiam a tabela de destino dentro de um gatilho antes da inserção resultam em um impasse

  • 772

Estou trabalhando em um aplicativo da web que usa um banco de dados PostgreSQL para armazenamento. Existem muitas entidades lógicas neste aplicativo que estão relacionadas entre si hotel --> hotel_floor --> hotel_room(o exemplo de hotéis usado ao longo da pergunta é puramente hipotético). Cada tabela armazena os IDs de todos os seus pais na hierarquia junto com seu próprio ID que é único no escopo de seus pais . A estrutura do banco de dados é mais ou menos assim:

table hotel (
    hotel_id int,
    ...

    CONSTRAINT hotel_pk PRIMARY KEY (hotel_id)
)


table floor (
    hotel_id int,
    floor_id int,
    ...

    CONSTRAINT floor_pk PRIMARY KEY (hotel_id, floor_id),
    CONSTRAINT floor_hotel_fk FOREIGN KEY (hotel_id) REFERENCES hotel (hotel_id)
)


table room (
    hotel_id int,
    floor_id int,
    room_id int,
    ...

    CONSTRAINT room_pk PRIMARY KEY (hotel_id, floor_id, room_id),
    CONSTRAINT room_hotel_fk FOREIGN KEY (hotel_id) REFERENCES hotel (hotel_id),
    CONSTRAINT room_floor_fk FOREIGN KEY (hotel_id, floor_id) REFERENCES floor (hotel_id, floor_id),
)

E os registros armazenados lá têm IDs que se parecem com isso:

  hotel
--------
hotel_id
--------
   1
   2
   3


        floor
-------------------
hotel_id | floor_id
-------------------
    1    |    1
    1    |    2
         |
    2    |    1
    2    |    2


             room
-----------------------------
hotel_id | floor_id | room_id
-----------------------------
    1    |    1     |    1
    1    |    1     |    2
    1    |    1     |    3

    1    |    2     |    1
    1    |    2     |    2
    1    |    2     |    3
    1    |    2     |    4

Obviamente, por causa dessa estrutura de banco de dados (que foi herdada por nossa equipe e não é fácil de refatorar), todos os IDs precisam ser gerados manualmente. Por esse motivo, temos um gatilho de inserção anterior em cada tabela, que se resume a algo semelhante a isto:

CREATE OR REPLACE FUNCTION room_set_new_id() RETURNS trigger LANGUAGE plpgsql AS $
BEGIN
    LOCK TABLE room IN SHARE ROW EXCLUSIVE MODE;

    NEW.room_id = (
        SELECT COALESCE(MAX(room_id), 0) + 1
        FROM room
        WHERE hotel_id = NEW.hotel_id AND floor_id = NEW.floor_id
    );

    RETURN NEW;
END
$;

CREATE TRIGGER room_set_new_id
BEFORE INSERT ON room
FOR EACH ROW EXECUTE PROCEDURE room_set_new_id();

O problema está na LOCK TABLEdeclaração. Até recentemente, não havia nenhum, mas começamos a receber erros de chave duplicada quando 2 operações de inserção eram executadas ao mesmo tempo, então decidi adicionar esses bloqueios na esperança de corrigir o problema. Infelizmente, isso levou a um novo problema: as inserções simultâneas agora resultam em um impasse. Eu experimentei com o código do servidor e descobri que o problema reside exclusivamente nos próprios 's, por exemplo , INSERTduas ou mais INSERT INTO room (hotel_id, floor_id) VALUES (1, 1)consultas emitidas ao mesmo tempo - não há outras operações de leitura/gravação acontecendo com o hotel//floorroomtabelas naquele momento (na verdade, cada solicitação ao servidor web vem de um usuário logado, e há uma conexão entre a tabela de usuários e a tabela de hotéis que é usada para fins de autenticação, mas não acho que um simultâneo "verifique se o usuário atual está relacionado ao hotel em questão" a consulta deve afetar a operação de inserção).

Eu tenho tentado descobrir como consertar isso, mas sem sucesso. É claro que posso adicionar tratamento de erro de bloqueio no servidor da Web, detectando o erro e tentando novamente a execução da consulta, mas prefiro fazer isso apenas se não houver realmente nenhuma maneira de resolver esse problema no nível do banco de dados, ou seja, se o bloqueio da minha tabela estratégia está errada e há uma maneira diferente de evitar a geração de IDs duplicados, gostaria de fazer isso. (Lembre-se, estou preso a essa estrutura de banco de dados e levará muito tempo e esforço para reescrever toda a lógica do banco de dados e do servidor da Web para usar colunas geradas automaticamente apropriadas).

postgresql
  • 1 1 respostas
  • 20 Views

1 respostas

  • Voted
  1. Best Answer
    Alex F
    2023-08-11T22:13:38+08:002023-08-11T22:13:38+08:00

    Depois de mais algumas experiências, descobri a origem do problema - bloqueio sendo adquirido durante a execução do gatilho. Não sei qual é o motivo aqui, mas, aparentemente, isso leva a conflitos entre quaisquer operações que o banco de dados esteja executando durante essas transações simultâneas. Portanto, executar LOCK TABLEantes INSERTcorrige o problema completamente.

    Gatilho fixo:

    CREATE OR REPLACE FUNCTION room_set_new_id() RETURNS trigger LANGUAGE plpgsql AS $
    BEGIN
        NEW.room_id = (
            SELECT COALESCE(MAX(room_id), 0) + 1
            FROM room
            WHERE hotel_id = NEW.hotel_id AND floor_id = NEW.floor_id
        );
    
        RETURN NEW;
    END
    $;
    
    CREATE TRIGGER room_set_new_id
    BEFORE INSERT ON room
    FOR EACH ROW EXECUTE PROCEDURE room_set_new_id();
    

    E exemplo de transação:

    BEGIN;
    LOCK TABLE room IN SHARE ROW EXCLUSIVE MODE;
    INSERT INTO room (hotel_id, floor_id) VALUES (1, 1);
    COMMIT;
    
    • 0

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