MySQL生产者消费者有多个选择线程

时间:2014-08-21 13:10:52

标签: mysql sql multithreading locking

我有以下情况:

有一张表格包含不同的"工作"处理和几个工作线程消耗这些工作。 由于我不想在完成后删除这些工作,我只会设置一个完整的"该记录的标志。

所以实际上我已经完成了以下工作流程(针对每个处理线程)

  1. 选择未完成的第一条记录
  2. 处理工作
  3. 设置"完成"标志
  4. 如何防止其他线程使用相同的作业(将其设置为"完成"将需要一段时间)。还只是更新"完成"第二步中的标志将导致一些作业被处理两次,因为可能有大量的线程处理少量的作业。

    最简单的方法是仅锁定记录(是的,我使用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!

1 个答案:

答案 0 :(得分:2)

我有一个实用的解决方案,我在工作场所的项目中看到了这个解决方案。而不是仅使用0和1表示不完整和已完成,请展开您的集合以包含更多案例。

我们将该列称为状态。以下是该列的不同值以及作业的相应状态。

  1. 当状态为0时,任何工作线程都没有接收作业。
  2. 当状态为1时,作业已被工作线程拾取并正在处理中。
  3. 当状态为2时,作业失败。 (您应该考虑处理失败的可能性。)
  4. 当状态为3时,作业已完成。
  5. 您的线程应包含逻辑,以便它只获取状态为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;