我有以下情况:
有一张表格包含不同的"工作"处理和几个工作线程消耗这些工作。 由于我不想在完成后删除这些工作,我只会设置一个完整的"该记录的标志。
所以实际上我已经完成了以下工作流程(针对每个处理线程)
如何防止其他线程使用相同的作业(将其设置为"完成"将需要一段时间)。还只是更新"完成"第二步中的标志将导致一些作业被处理两次,因为可能有大量的线程处理少量的作业。
最简单的方法是仅锁定记录(是的,我使用InnoDB),这样其他线程就无法读取该记录集。这可以通过" FOR UPDATE"轻松实现,但这将锁定整个表以供将来选择和所有其他选择" FOR UPDATE"选择必须等到第一个完成。
任何人都可以告诉我,如何在不延迟所有其他线程的情况下解决这种情况?所以实际上选择(也可以限制为LIMIT 1)只应该"参见"非锁定行...
示例表结构将类似于
JobID | completed
123 | 0
124 | 1
125 | 0
用大约10-50个线程做一个简单的
SELECT JobID from jobs WHERE completed = 0;
UPDATE jobs SET completed = 1 WHERE JobID = ?;
任何提示和技巧的Thx!
答案 0 :(得分:2)
我有一个实用的解决方案,我在工作场所的项目中看到了这个解决方案。而不是仅使用0和1表示不完整和已完成,请展开您的集合以包含更多案例。
我们将该列称为状态。以下是该列的不同值以及作业的相应状态。
您的线程应包含逻辑,以便它只获取状态为0的作业并将状态更改为1.这将禁止其他线程获取正在处理的作业。作业完成后,状态将设置为3,如果作业失败,则状态设置为2.然后线程可以继续查找另一个仍有待完成的作业。
您还可以要求线程考虑获取状态2的作业,但您必须定义逻辑以指定有限数量的重试。
修改强>
在long discussion之后,我们偶然发现了解决方案。当“工作”工作时,我的上述答案在更广泛的状态下是好的。是一个需要一些时间才能完成的过程。但OP的问题并非如此。
所以最终有效的解决方案是:
BEGIN
SELECT * FROM Jobs WHERE JobID = (SELECT * FROM Jobs WHERE completed = 0 LIMIT 1) LOCK IN SHARE MODE;
UPDATE Jobs SET completed = 1 WHERE JobID = (PREVIOUS ID);
COMMIT;