当前位置: 首页 > 知识库问答 >
问题:

MySQL-尝试获取锁时发现死锁

谭灿
2023-03-14

tt_usersid作为主键,列state(CHAR(1))可以是“x”或“y”,以及state_position(INT)。这些列上没有索引。存储引擎是InnoDB。

state_positions必须总是连续的,并且对于某个状态可能永远不会有重复的位置,即如果我有5个状态为“x”的用户,他们的state_positions必须是1、2、3、4、5

这是我正在运行的导致死锁的查询:

insert into `tt_users` (`state`, `state_position`) 
values ('x',
   (SELECT MAX(state_position) AS maxStatePosition
    FROM tt_users AS u2
    WHERE u2.state='x') + 1
)

为了测试,我同时插入了大量的用户,但每次都出现死锁错误。

我读了这篇文章--如何避免mysql在尝试获得锁时发现的死锁;try restarting Transaction“但是我不明白应该如何处理查询来防止死锁,如果这是可能的,因为这个问题的答案--解决MySQL错误”在尝试获取锁时发现死锁;try restarting Transaction“--说死锁无论如何都可能发生。

我设法使其工作并且始终如一地工作的唯一方法是这样的(语言是PHP):

PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

然后手动锁定表,并运行我的查询:

SET autocommit=0;

LOCK TABLES
tt_users WRITE,
tt_users AS u2 WRITE;

insert into `tt_users` (`state`, `state_position`) 
values ('x',
   (SELECT MAX(state_position) AS maxStatePosition
    FROM tt_users AS u2
    WHERE u2.state='x') + 1
);

COMMIT;
UNLOCK TABLES;

后来呢:

PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

使用该方法,我同时运行代码4次,每个实例插入2500个用户,没有出现任何问题。

这是使其工作的唯一方法吗?还是我可以100%确定地防止死锁而不必手动锁定表?

更新:

按照@wallyk的回答,我试了以下几个:

1)将查询放入事务中-仍然死锁错误
2)使用with confired snapshotread writeread write启动事务。所有三个选项都需要设置pdo::setAttribute(Pdo::attr_emulate_prepares,true);with confired snapshotread write仍然给我死锁错误,而read only甚至不允许

因此,目前看来手动锁定表是唯一有效的方法。

共有1个答案

宦源
2023-03-14

我不知道这是否会有帮助,但是用事务包围SQL语句可能会在内部提供正确的锁定顺序:

start transaction;
insert into `tt_users` (`state`, `state_position`) 
values ('x',
   (SELECT MAX(state_position) AS maxStatePosition
    FROM tt_users AS u2
    WHERE u2.state='x') + 1
);
commit;

如果这不起作用,可以将一些选项添加start transaction语句中。看这里。

 类似资料:
  • 问题内容: 我在项目中使用了hibernate模式,并且由于非常简单的数据库操作而获得了随机的表观死锁。 有一个堆栈跟踪:https : //gist.github.com/knyttl/8999006 –让我感到困惑的是,第一个异常是RollbackException,然后是LockAquisition异常。 问题经常发生在类似的条款上: 我很困惑,因为我不知道这是Hibernate,MySQL

  • 我得到以下错误当有多个更新运行特定的表数据库-mysql-innodb引擎语言php-pdo 错误-序列化失败:尝试获取锁时发现死锁 我检查了下面的链接SQLState[40001]:序列化失败:尝试获取锁时发现1213死锁;尝试按Magento位置重新启动事务

  • 问题内容: 我有一个带有约5,000,000行的MySQL表,该表通过通过DBI连接的并行Perl进程以小方式不断更新。该表有大约10列和几个索引。 一种相当常见的操作有时会导致以下错误: 触发错误的SQL语句如下所示: 该错误仅在某些时候触发。我估计会减少1%的来电。但是,使用小表从未发生这种情况,随着数据库的增长,它变得越来越普遍。 请注意,我正在使用file_table中的a_lock字段来

  • 问题内容: 我的应用程序(java spring-core)有多个线程同时运行并访问数据库,在某些高峰时间出现异常 我的代码看起来 问题答案: MySQL的InnoDB引擎具有行级锁定功能,即使您的代码正在插入或更新单个行(尤其是要更新的表上有多个索引),也可能导致死锁。最好的选择是围绕它设计代码,以便在由于死锁而失败的情况下重试事务。一些有关MySQL死锁诊断的有用信息以及可能的解决方法在此处提

  • 问题内容: 我有一个记录在线用户的innoDB表。用户每次刷新页面时都会对其进行更新,以跟踪他们所访问的页面以及它们对该站点的最后访问日期。然后,我会有一个cron,每15分钟运行一次,以删除旧记录。 我在尝试获取锁时发现了“死锁;昨晚尝试重新启动事务”大约5分钟,这似乎是在向该表中运行INSERT时出现的情况。有人可以建议如何避免此错误吗? ===编辑=== 以下是正在运行的查询: 首次访问网站

  • 如何理解死锁的原因--即,如何找出哪些事务捕获了哪些锁? 我的Engine.log文件存在以下死锁: 我对日志中描述的内容的看法如下: ***(2)保持锁 记录锁空间id 0页号36025889 n位96表MYDB.MYTABLE trx id 4 271 9072205锁模式X锁rec,但不锁gap 记录锁,堆第27号物理记录:N_Fields72;紧凑格式;信息位0 ***(1)等待授予此锁: