我正在使用mysql构建一个“穷人排队系统”。它是一个包含需要执行的作业的表(表名为 queue
). 我在多台机器上有几个进程,它们的任务是调用 fetch_next2
存储过程以从队列中获取项目。
这个过程的重点是确保我们永远不会让两个客户得到相同的工作。我想通过使用 SELECT .. LIMIT 1 FOR UPDATE
将允许我锁定一行,这样我就可以确保它只由一个调用者更新(更新到不再符合 SELECT
用于筛选“准备好”进行处理的作业)。
有人能告诉我我做错了什么吗?我只是有一些例子,同一个工作被分配给两个不同的进程,所以我知道它不能正常工作。:)
CREATE DEFINER=`masteruser`@`%` PROCEDURE `fetch_next2`()
BEGIN
SET @id = (SELECT q.Id FROM queue q WHERE q.State = 'READY' LIMIT 1 FOR UPDATE);
UPDATE queue
SET State = 'PROCESSING', Attempts = Attempts + 1
WHERE Id = @id;
SELECT Id, Payload
FROM queue
WHERE Id = @id;
END
1条答案
按热度按时间mspsb9vt1#
答案代码:
您所做的工作的问题是没有用于操作的原子分组。您正在使用选择。。。更新语法。文档说它阻止了“在特定事务隔离级别读取数据”。但不是所有级别(我认为)。在第一次选择和更新之间,可以从另一个线程执行另一次选择。你用的是myisam还是innodb?myisam可能不支持。
确保此操作正常工作的最简单方法是锁定表。
[编辑]我在这里描述的方法比使用
Id=(SELECT @id := Id)
方法。另一种方法是:
具有通常设置为0的列。
执行“更新…”。。。设置colname=uniq\u id,其中colname=0限制1。这将确保只有一个进程可以更新该行,然后通过select获取它(uniq\u id不是mysql特性,只是一个变量)
如果你需要一个唯一的id,你可以使用一个自动递增的表。
你也可以用事务来实现这一点。如果在表上启动事务,请运行
UPDATE foobar SET LockVar=19 WHERE LockVar=0 LIMIT 1;
从一个线程开始,并在另一个线程上执行完全相同的操作,第二个线程将等待第一个线程提交,然后再获取其行。不过,这最终可能是一个完整的表阻塞操作。