我正在研究一个多次问自己的php程序,导致几个工作进程然后自动处理任务表。每个进程打开它自己的mysql连接(这是必须的,因为php的forking体系结构),然后应该处理它自己的任务(没有两个工作者应该在同一个任务上工作)。为此,工作人员在开始处理之前“接受”任务。这是通过以下方式实现的:
where status = 'pending'
,以确保在此期间未执行此操作。我首先在mysql shell中测试了这个场景 - 在两个终端窗口中建立了两个与数据库的连接。两者都开始了交易。然后在第一个窗口中将任务更新为“已拍摄”。尝试在第二个窗口中更新相同的任务。然后第二个窗口等待,直到我在第一个窗口中提交事务并且只是礼貌地失败(0行受影响)。
现在,在php(使用PDO)中,只要第二个进程尝试执行任务,我就会在db上遇到死锁:
PHP致命错误:未捕获异常'PDOException',消息为'SQLSTATE [40001]:序列化失败:1213尝试获取锁定时发现死锁;尝试重启事务'in ...
我不确定我是否应该只在事务中包装实际的更新任务。但是在我看来,我在程序中执行它的方式与我在shell上执行它的方式相同,因此应该可以工作。
有人可以帮我解决我在这里缺少的事情吗?
答案 0 :(得分:0)
你在InnoDB这样很好。
你的任务是否在桌子上?我认为他们是,但你没有这么明确地说。
尝试使用此规则来获取要工作的任务。
BEGIN; /* start a transaction */
SELECT task_id, whatever
FROM tasktable
WHERE status = 'pending'
LIMIT 1
FOR UPDATE;
然后,如果您返回一个带有任务ID的行,您就可以处理该任务了。如果没有返回任何行,则没有任何待处理的任务。
将任务标记为有效。
UPDATE tasktable
SET status = 'working'
WHERE task_id = <<the task ID you just got back>>;
然后,
COMMIT;
如果你遇到一个死锁异常(你可能仍会得到一个,但会更少)然后发出ROLLBACK;
,然后再次尝试整个序列。
然后,完成任务。完成后将其标记为已完成。您不需要事务,因为您可以在一个查询中执行此操作。
UPDATE tasktable
SET status = 'complete'
WHERE task_id = <<the task ID you just got back>>;
如果您在客户端连接中关闭了自动提交,请不要忘记在该查询后提交。
答案 1 :(得分:0)
我找到了"solution" that I'm going with。如果有人有不同的解决方案,我很乐意听到它,如果我最终使用它,我会乐意接受你的答案。
所以基本上试图在循环中“接受”任务,当我遇到死锁时,我会回滚并再试一次。我的第一次测试表明,它解决了所有工人试图完成任务时的初始问题。