我有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;
在此之后,脚本正在向该产品的网址发出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锁,从状态为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)中的死锁,所以,我不得不将代码更改为表级锁定
由于
答案 0 :(得分:1)
你在这里做错了几件事。首先,没有理由让你锁定表格。 InnoDB的设计目标之一是它具有行级锁定。
其次,您应该使用SELECT FOR UPDATE语句来锁定您正在使用的行,然后执行UPDATE和COMMIT。
当这些表只是关于此更新的信息时,我也不明白为什么要将产品加入类别和菜单。这看起来像是一个“数据处理”功能。