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 / 337710
Accepted
J.D.
J.D.
Asked: 2024-03-14 05:46:16 +0800 CST2024-03-14 05:46:16 +0800 CST 2024-03-14 05:46:16 +0800 CST

A condição de corrida de implementação da minha tabela de filas é segura?

  • 772

Olá pessoas mais espertas que eu! Eu criei uma espécie de sistema de tabela de fila, mas parece simples demais para estar protegido contra condições de corrida. Estou faltando alguma coisa ou a seguinte condição de corrida é segura?

O Esquema

Eu tenho uma mesa, vamos chamá-la ProductQueue:

CREATE TABLE dbo.ProductQueue
(
    SerialId BIGINT PRIMARY KEY,
    QueuedDateTime DATETIME NOT NULL -- Only using this for reference, no functionality is tied to it
);

Eu tenho um procedimento para adicionar à fila chamado AddToProductQueue:

CREATE PROCEDURE dbo.AddToProductQueue (@SerialId BIGINT)
AS
BEGIN
    INSERT INTO dbo.ProductQueue (SerialId, QueuedDateTime)
    OUTPUT Inserted.SerialId
    SELECT @SerialId, GETDATE();
END

Também tenho um procedimento para remover da fila chamado RemoveFromProductQueue:

CREATE PROCEDURE dbo.RemoveFromProductQueue (@SerialId BIGINT)
AS
BEGIN
    DELETE FROM dbo.ProductQueue
    OUTPUT Deleted.SerialId
    WHERE SerialId = @SerialId;
END

Observe SerialIdque é globalmente exclusivo para um Productbanco de dados/sistema de origem. Ou seja, duas instâncias de a Productnunca podem ter o mesmo SerialId. Essa é a extensão do lado do banco de dados.

O fluxo de trabalho

  • Eu tenho um processo de inscrição que é executado de hora em hora.
  • Esse processo obtém uma lista de variáveis SerialIds​​do sistema de origem.
  • Ele chama iterativamente o AddToProductQueueprocedimento em cada um SerialIdde sua lista.
  • Se o procedimento tentar inserir um SerialIdque já existe na ProductQueuetabela, ele gerará um erro de violação de chave primária e o processo do aplicativo capturará esse erro e o ignorará SerialId.
  • Caso contrário, o procedimento adiciona isso SerialIdà ProductQueuetabela com êxito e o retorna ao processo de aplicativo.
  • O processo de inscrição então adiciona o que foi enfileirado com sucesso SerialIdem uma lista separada.
  • Depois que o processo de aplicativo termina de iterar sua lista de todos os candidatos SerialIdsa serem enfileirados, ele itera sua nova lista de enfileirados com sucesso SerialIdse realiza trabalho externo neles, em um thread separado por SerialId. (Este trabalho não está relacionado ao banco de dados.)
  • Finalmente, à medida que cada thread termina seu trabalho externo, a última etapa desse thread assíncrono é removê-lo SerialIdda ProductQueuetabela chamando o RemoveFromProductQueueprocedimento. (Observe que um novo objeto de contexto de banco de dados é instanciado e uma nova conexão é criada para cada chamada assíncrona para esse procedimento, para que ele seja thread-safe no lado do aplicativo.)

Informações adicionais

  • Não há índices na ProductQueuetabela e ela nunca terá mais de 1.000 linhas ao mesmo tempo. (Na verdade, na maioria das vezes terá literalmente apenas algumas linhas.)
  • O mesmo SerialIdpode se tornar candidato novamente para ser adicionado novamente à tabela de filas em uma futura execução do processo de aplicação.
  • Não há proteções que impeçam a execução simultânea de uma segunda instância do processo de aplicativo, seja por acidente ou se a primeira instância demorou mais de 1 hora para ser executada, etc. (Esta é a parte simultânea com a qual estou mais preocupado.)
  • O nível de isolamento da transação do banco de dados (e da conexão que está sendo feita) onde residem a tabela de filas e os procedimentos é o nível de isolamento padrão de Read Committed.

Problemas potenciais

  • A instância em execução do processo do aplicativo trava de forma não tratada, deixando- SerialIdsa presa na tabela de filas. Isso é aceitável para as necessidades do negócio e planejamos ter relatórios de exceção para nos ajudar a remediar manualmente esse caso.
  • O processo do aplicativo é executado várias vezes simultaneamente e captura algumas das mesmas SerialIdsentre as instâncias em suas listas de origem iniciais. Ainda não consigo pensar em nenhuma ramificação negativa deste caso, uma vez que o procedimento de enfileiramento é atômico, e a lista real em SerialIdsque o processo de aplicação funcionará deve ser independente devido a esse procedimento de enfileiramento atômico. Não nos importamos qual instância do processo de aplicativo realmente processa cada uma SerialId, desde que a mesma SerialIdnão seja processada simultaneamente por ambas as instâncias do processo.
sql-server
  • 4 4 respostas
  • 863 Views

4 respostas

  • Voted
  1. Best Answer
    Paul White
    2024-03-14T17:09:44+08:002024-03-14T17:09:44+08:00

    A única coisa que você está pedindo ao mecanismo de banco de dados neste cenário é impor o PRIMARY KEY. Fá-lo-á em todas as condições e níveis de isolamento, claro.

    As possíveis condições de corrida são todas externas ao banco de dados, considerações que não são abordadas aqui.

    Dito isso, a única maneira de pensar no banco de dados envolvido em uma condição de corrida é o processo de adição de IDs seriais candidatos a serem agrupados em uma transação de banco de dados , mas você não mencionou nada sobre isso.

    Talvez seja possível que os processos usem uma transação de banco de dados de maneira não intencional, por exemplo, se você estiver usando um ORM que faz coisas úteis por mágica, sem ser solicitado explicitamente. Ou talvez você esteja usando transações implícitas.

    Nesse cenário, a instância do aplicativo A começaria a adicionar seus (longa lista de) IDs seriais, todos em uma única transação de banco de dados. Enquanto isso, a instância do aplicativo B (com uma lista igualmente longa, incluindo pelo menos uma presente na lista de A) seria bloqueada na inserção de um ID serial sobreposto (porque a instância A ainda não confirmou sua transação).

    Em uma sequência infeliz de eventos, a instância A terminaria o processamento assíncrono de um ID serial duplicado (no início de sua lista) antes que a instância B bloqueada pudesse executar sua inserção (no final de sua lista).

    Nesse caso, A e B processariam com êxito o mesmo ID serial.

    Isto parece improvável, dado o esboço do processo que você descreveu, mas não impossível.

    • 6
  2. Quassnoi
    2024-03-15T02:41:43+08:002024-03-15T02:41:43+08:00

    Não sei por que o nome da sua tabela contém a palavra "fila", mas parece-me que o que você realmente deseja é um mecanismo de bloqueio.

    Para responder diretamente à sua pergunta: se você não se importa com bloqueios obsoletos e desde que a INSERTtransação, o processamento e a DELETEtransação estejam em uma relação causal (ou seja, aconteça nesta ordem após a etapa anterior relatar sucesso), eu não não vejo nenhum potencial para processamento duplo.


    Dito isso, uma maneira de resolver os dois problemas listados em "problemas potenciais" de maneira automatizada seria implementar a variante de instância única do algoritmo de bloqueio Redlock .

    1. Cada instância tem seu próprio ID exclusivo (digamos, um guia ou algo que você gera toda vez que executa seu aplicativo de processamento)

    2. Sempre que a instância captura um SerialId, ela tenta obter um bloqueio sobre ele:

      CREATE TABLE ItemLock (serialId BIGINT NOT NULL PRIMARY KEY, instance UNIQUEIDENTIFIER NOT NULL, expires DATETIME NOT NULL);
      
      WITH    source (serialId, instance, expires) AS
              (
              SELECT  @serialId, @instance, @expires
              )
      MERGE
      INTO    ItemLock target
      USING   source
      ON      target.serialId = source.serialId
      WHEN NOT MATCHED BY TARGET THEN
      INSERT  (serialId, instance, expires)
      VALUES  (serialId, instance, expires)
      WHEN MATCHED AND (target.instance = source.instance OR target.expires <= @now) THEN
      UPDATE
      SET     instance = source.instance,
              expires = source.expires
      OUTPUT  INSERTED.serialId
      

      A saída da consulta, se houver, será o ID de série no qual você está bloqueado. Se não houver saída, você não tem o bloqueio.

      Passe o timestamp atual na variável @nowe o timestamp de cinco minutos no futuro na variável@expires

    3. Execute um thread keep-alive que envie essa consulta a cada minuto ou com a freqüência necessária, estendendo o valor na variável @expires, digamos, cinco minutos depois de cada vez. Você pode alterar a consulta para estender bloqueios que ainda não foram processados ​​em um único lote. Certifique-se de se comprometer imediatamente.

    4. Quando terminar o processamento, exclua o cadeado apenas se for seu :

      DELETE
      FROM    ItemLock
      WHERE   serialId = @serialId
              AND instance = @instance
      

    Por aqui:

    1. Se o seu aplicativo morrer, o bloqueio morrerá sozinho em 5 minutos.
    2. Nenhum processamento simultâneo acontecerá, desde que você obtenha o bloqueio com sucesso e o mantenha ativo.

    Claro, você pode simplesmente instalar uma instância real do Redis e usar uma implementação pronta do Redlock em seu aplicativo, que é abundante.

    • 2
  3. S. Rojak
    2024-03-16T05:55:58+08:002024-03-16T05:55:58+08:00

    Achei o Service Broker completamente insatisfatório, impondo muita sobrecarga. As conversas exigem limpeza. Você está melhor com sua mesa.

    Eu também recomendaria permitir bloqueios de linha no índice atrás de sua chave primária.

    • 0
  4. riccardo
    2024-03-15T17:33:16+08:002024-03-15T17:33:16+08:00

    Não há nada de errado com o que você está fazendo, mas o SQL Server já possui um serviço integrado, Broker Services, que trata e gerencia filas de dados. Eu humildemente sugeriria que você investisse seu tempo nisso. Obrigado 😀

    • -1

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

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