Mysql - 序列化失败: 1213 发现死锁,尝试重新启动事务。

7

我有菜单分类产品表。我正在使用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锁,从产品表中获取一个状态为0(表示-待办事项)的行,尝试将其状态设置为1(表示待处理),然后解锁表。之后尝试在php中执行一些逻辑,并最终尝试更新产品表-更新内容列以及状态为2(表示已完成)。
如果我并行运行5个脚本,在运行几分钟后进行的最后一步(将产品更新为已完成)会出现以下错误。
Error: SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when try to get lock; try restarting transaction

我知道当两个事务相互等待以相反的顺序更新相同的行时死锁的一般想法,但是我无法弄清楚在这种情况下死锁的原因。我的意思是脚本正在工作并按照相同的顺序锁定表,因此如果一个脚本已经锁定了products(和其他)表,并且正在获取排他锁,则另一个脚本应该在队列中等待这些锁被释放,因此不应该导致死锁。
另一方面,每个脚本都选择状态为-> 0的产品,并尝试将其更新为1,在同一“会话”中从1到2,因此我不明白这可能是死锁的原因。我错过了什么?
编辑:
虽然我没有提及如何使用类别和菜单表的信息,但我确实需要获取它们,它们的使用不重要,因为我没有对它们进行任何db处理。
我曾经使用行级锁定SELECT FOR UPDATE,但我像在这个问题中MySQL InnoDB dead lock on SELECT with exclusive lock (FOR UPDATE),所以我不得不将代码更改为表级锁定
谢谢
1个回答

2
这里有几件事情你做得不对。首先,没有理由锁定表格。InnoDB 的一个设计目标是它具有行级锁定。
其次,应该使用 SELECT FOR UPDATE 语句来锁定你要处理的行,然后执行 UPDATE 和 COMMIT。
我也不明白为什么要将产品连接到类别和菜单,当这些表格在此更新中只是提供信息。这似乎是一个“数据处理”功能。

谢谢你的回答,事实上我一直在使用select for update,但是我遇到了这个死锁问题https://dev59.com/IVXTa4cB1Zd3GeqP25Oq#5432714,因为我无法删除主键(如该答案所述),所以我决定进行表级别的锁定。至于类别和手册,我只是想描述它们的工作原理,以免漏掉任何东西,这些表格的信息是必要的,我不是没有理由地获取它们,但这些信息对我来说是不必要的,所以我没有在问题中包含它们。 - dav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接