我有menus
,categories
和products
表。我正在使用 mysql 5.5,所有表都是 innoDB,并且在所有情况下 id 都是具有自动增量的主键(int)。
menus table
id, name, status
categories table
id, menu_id, name
products table
id, menu_id, category_id, status, name, url, content
多个脚本可以并行运行,执行包含以下逻辑的同一个 php 文件。
START TRANSACTION;
SET autocommit = 0;
LOCK TABLES products WRITE, categories WRITE, menus WRITE;
SELECT
p.`id`,
p.`name`,
p.`url`,
p.`status`,
c.`id` cat_id,
c.`name` cat_name,
m.`id` `menu_id`,
m.`name` menu_name
FROM
products p
LEFT JOIN categories c
ON p.`subcategory_id` = c.`id`
LEFT JOIN menus m
ON p.`menu_id` = m.`id`
WHERE p.`status` = 0 LIMIT 1
如果找到选择结果后,它的状态更新为1(否则我回滚并释放锁)
UPDATE products SET status = 1 WHERE id = [product_id];
如果最后一个查询成功,我运行
COMMIT();
UNLOCK TABLES;
否则
ROLLBACK();
UNLOCK TABLES;
在此之后,脚本向该产品的 url 发出 curl 请求,以从中获取一些内容并相应地更新产品的行
// making curl request (might take a few dozen seconds, because proxy is being used and sometimes because of proxy failure the request is attempted again)
trying to update the products table
UPDATE products SET content = [received content], status = 2 WHERE id = [product_id]
因此,脚本在上述表上获取 X 锁,从 products 表中获取状态为 0(表示 - TODO)的一行,尝试将其状态设置为 1(表示 PENDING),然后解锁表。之后尝试在 php 中执行一些逻辑,最后尝试更新产品表 - 将内容列和状态更新为 2(表示完成)。
如果我并行运行 5 个脚本,在最后一步运行几分钟后(将产品更新为 DONE),我会收到此错误
Error: SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when try to get lock; try restarting transaction
当 2 个事务相互等待以相反的顺序更新相同的行时,我理解死锁的一般概念,但是在这种情况下我无法弄清楚死锁的原因。我的意思是脚本正在工作并以相同的顺序锁定表,因此如果一个脚本已锁定products
(和其他)表,获取排他锁,另一个脚本应该在队列中等待这些锁被释放,所以它不应该导致死锁.
另一方面,每个脚本都在选择状态为 -> 0 的产品并尝试更新为 1,并且在从 1 到 2 的同一“会话”期间,所以我看不出这如何成为死锁的原因。我在这里想念什么?
编辑:
虽然我没有提到我如何使用类别和菜单表的信息,但我确实需要获取它们,它们的使用并不重要,因为我没有对它们进行任何数据库处理。
我曾经使用行级锁定SELECT FOR UPDATE
,但是我遇到了像这个问题MySQL InnoDB dead lock on SELECT with exclusive lock (FOR UPDATE)这样的死锁,所以,我不得不将代码更改为表级锁定
谢谢