Isso está causando um impasse porque o
UPDATE
query está bloqueando todas as linhas da tabela e, dependendo dos índices usados (ou da falta deles), duas sessões diferentes potencialmente os bloquearão em uma ordem ligeiramente diferente. Lembre-se que UPDATE
, DELETE
e SELECT ... FOR UPDATE
bloqueará todas as linhas que encontrarem, se essas linhas corresponderem a todos os WHERE
condições ou não. Portanto, ao usá-los, você deve se esforçar para garantir que eles encontrem o menor número de linhas possível, usando índices (idealmente a chave primária) e evitando condições vagas ou de seleção ampla. Minha sugestão para filas de trabalho é praticamente universal:bloqueie o mínimo possível, o mais raramente possível e sempre em uma ordem determinística. Então, geralmente:
- Use leituras sem travamento (regular
SELECT
) para encontrar trabalho para fazer procurando por coisas que seu funcionário sabe fazer e não foi reivindicado no momento (lease_owner IS NULL AND lease_expiry IS NULL
-- ou similar). - Escolha um item de trabalho (ou alguns, se tiver coragem, mas um é muito mais simples e normalmente permite um desempenho perfeitamente aceitável).
- Atualize seu item de trabalho (para reivindicá-lo, mas, em qualquer caso, ele também precisa ser atualizado):
- Abra uma transação.
- Bloqueie o item de trabalho escolhido com
SELECT ... FOR UPDATE
-- Se não for mais reclamado, cancele e escolha outro. - Atualize o item de trabalho escolhido com seu ID de trabalhador e um prazo de validade para o seu aluguel.
- Confirme sua transação imediatamente.
- Comece a trabalhar em seus itens de trabalho alugados.
- Em algum outro processo, outro pesquisador procura um trabalho abandonado e o cancela (pelo mesmo processo de atualização acima).
Você pode facilmente obter uma taxa de transferência muito alta com esse design (milhares de trabalhos por segundo) e essencialmente sem contenção e sem problemas de pedido. As otimizações para escolher o trabalho com menos probabilidade de entrar em conflito com outros pesquisadores são simples e eficazes (por exemplo, módulo no ID do trabalho ou similar, escolhido para evitar a fome de trabalhos). A chave é lembrar que o conflito na seleção do trabalho é ok -- apenas aborte e tente novamente e tudo se move muito rapidamente.
Todas as gravações de bloqueio para itens/trabalhos da fila de trabalho devem ser feitas apenas em linhas únicas e por chave primária somente .