gpt4 book ai didi

php - 多个进程更新相同的行会导致mysql死锁?

转载 作者:行者123 更新时间:2023-11-29 23:29:44 24 4
gpt4 key购买 nike

我正在开发一个 php 程序,该程序会多次 fork 自身,从而产生多个工作进程,然后这些进程自动处理任务表。每个进程都会打开它自己的 mysql 连接(由于 php 的 fork 架构,这是必须的),然后应该执行它自己的任务(任何两个工作进程都不应该执行同一个任务)。为此,工作人员在开始处理任务之前“接受”任务。这是通过以下方式实现的:

  1. 工作人员请求任务
  2. 启动一个事务,其中查询所有“待处理”任务
  3. 这些任务是迭代的。然后,脚本尝试通过更新其状态来“接受”每个任务。更新有一个条件 where status = 'pending' 以确保在此期间尚未进行更新。
  4. 一旦成功“执行”任务,迭代就会停止并提交事务。

我首先在 mysql shell 中测试了这个场景 - 在两个终端窗口中与数据库建立了两个连接。双方开始交易。然后在第一个窗口中将任务更新为“已执行”。尝试在第二个窗口中更新相同的任务。然后,第二个窗口等待我在第一个窗口中提交事务,然后礼貌地失败(0 行受影响)。

现在,在 php(使用 PDO)中,一旦第二个进程尝试执行任务,我就会在数据库上遇到死锁:

PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction' in ...

我不确定是否应该只将实际的更新要执行的任务包装在事务中。但在我看来,我在程序中执行此操作的方式与我在 shell 上执行的方式相同,因此应该可以工作。

有人可以帮我解决我在这里缺少的东西吗?

最佳答案

你在 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>>;

如果您在客户端连接中关闭了自动提交,请不要​​忘记在该查询之后提交。

关于php - 多个进程更新相同的行会导致mysql死锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26652515/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com