【发布时间】:2014-10-30 12:03:50
【问题描述】:
我正在开发一个 php 程序,该程序会多次分叉,从而产生几个工作进程,然后这些工作进程会自动处理一个任务表。每个进程打开它自己的 mysql 连接(这是必须的,因为 php 的分叉架构),然后应该处理它自己的任务(没有两个工作人员应该处理同一个任务)。为此,工人在开始处理之前“接受”一项任务。这是通过以下方式实现的:
- 工作人员请求任务
- 一个事务被启动,所有“挂起”的任务都被查询
- 这些任务是重复的。然后脚本尝试通过更新它的状态来“接受”每个任务。更新有一个条件
where status = 'pending',以确保它在此期间未被使用。 - 一旦成功“接受”任务,迭代就会停止并提交事务。
我首先在 mysql shell 中测试了这个场景——在两个终端窗口中建立了两个到数据库的连接。双方开始交易。然后在第一个窗口中将任务更新为“采取”。尝试在第二个窗口中更新相同的任务。然后第二个窗口一直等到我在第一个窗口中提交事务并且礼貌地失败了(0 行受影响)。
现在,在 php(使用 PDO)中,一旦第二个进程尝试执行任务,我就会在 db 上遇到死锁:
PHP 致命错误:未捕获异常 'PDOException' 并带有消息 'SQLSTATE[40001]:序列化失败:尝试获取锁时发现 1213 死锁;尝试在 ...中重新启动事务'
我不确定我是否应该只将实际的更新任务以采取包装在事务中。但在我看来,我在程序中执行的方式与我在 shell 上执行的方式相同,因此应该可以工作。
有人可以帮我解决我在这里缺少的东西吗?
【问题讨论】:
-
您的任务表的类型是什么:'MyISAM' 或 'InnoDB'?在您的情况下,必须设置“InnoDB”
-
死锁并不可怕,它们很正常。处理此类问题的通常方法是重新启动事务。您可以跟踪由于死锁而失败的次数,然后停止尝试。该任务将不完整,最终将由另一个工人接手。
-
@NB:你的意思是它基本上是 mysql 说“嘿,自从你开始交易以来,另一个进程已经更新了这一行”?。我将如何重新开始交易?
-
@HalayemAnis:是的 InnoDB。
-
我原以为死锁很少见,只要:您在“工作任务”中使用事务并以相同的顺序访问资源(表)。您可能会遇到“超时”。
标签: php mysql fork deadlock mariadb