Maneira correta de usar o MySQL para atribuir tarefas aos processos do trabalhador
-
21-08-2019 - |
Pergunta
Eu tenho uma grande lista de URLs em uma tabela MySQL Innodb e os processos de trabalhadores que consultam o MySQL para um conjunto de URLs processar. Os URLs devem ser marcados imediatamente como processados, para que outros processos do trabalhador não desperdiçam recursos começando a processar os mesmos.
Atualmente, faço isso pela primeira vez para obter alguns URLs:
SELECT DISTINCT url FROM urls WHERE task_assigned is NULL ORDER BY id LIMIT 100
Então, no código, eu ingenuamente percorriam cada um desses URLs para marcá -lo como processado:
UPDATE urls SET task_assigned = NOW() WHERE url = ? COLLATE utf8_bin
Estou perfeitamente ciente de como isso é bobo e ineficiente. Mais importante ainda, não há garantia de que outro processo de trabalhador não tentasse obter uma lista no meio das minhas atualizações. Qual é a bela maneira de fazer isso? Devo fazer uma transação, como?
Solução
O seguinte aparece (por uma rápida olhada no manual MySQL 5) para estar disponível no MySQL; Não tenho certeza se é a melhor abordagem, mas é uma que já usei antes no PostgreSQL:
BEGIN TRANSACTION;
SELECT DISTINCT url FROM urls WHERE task_assigned is NULL ORDER BY id LIMIT 100 FOR UPDATE;
UPDATE urls SET task_assigned = NOW() WHERE url IN [list of URLs] COLLATE utf8_bin;
COMMIT;
Na verdade, no PostgreSQL, eu usaria um solteiro Atualizar a instrução com a cláusula de retorno de atualização, substituindo o seleto, mas essa é uma extensão específica do PostGresql.
Um problema em potencial que vejo com sua abordagem são URLs duplicados: se URL http://www.example.com/
Aparece duas vezes na sua tabela, digamos com os IDs 23 e 42, ele será devolvido com um desses dois IDs pela seleção, mas a atualização afetará as duas linhas. Não sei se esse comportamento faz sentido em seu aplicativo; Eu provavelmente colocaria algum tipo de restrição única nos URLs para que não pudesse acontecer e depois usar uma lista de IDs, não URLs, no IN
Cláusula (que deveria ser mais rápida).
Outras dicas
Talvez você deva apenas selecionar todos os URLs primeiro e depois usar threads para analisá -los de forma assíncrona?