我有表tt_users
其中有id
作为主键,列state
(CHAR(1)),它可以是 “X” 或 “Y”,并state_position
(INT)找到。这些列上没有索引。存储引擎是innodb。MySQL的 - 死锁时,试图获得锁定
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
)
为了测试,我插入了大量用户同时的,每一次我得到了僵局错误。
我看到这篇文章 - How to avoid mysql 'Deadlock found when trying to get lock; try restarting transaction',但我不明白,我应该用我的查询,以防止死锁做,如果这是在所有可能的,因为这个问题的答案 - Working around MySQL error "Deadlock found when trying to get lock; 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次运行的代码,每个实例inser帮助2500名用户,没有问题。
是否只有这样才能使其发挥作用,或者我可以100%确定地防止死锁而不必手动锁定表?
UPDATE:
每@ wallyk的回答,我试过如下:
1)把查询在事务 - 仍然死锁错误
2)WITH CONSISTENT SNAPSHOT
开始交易,READ WRITE
, READ ONLY
。所有3个选项都需要设置PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
。 WITH CONSISTENT SNAPSHOT
和READ WRITE
仍然给我造成了死锁错误,而READ ONLY
自然也没有让我执行INSERT
。
所以现在看来手动锁定表是唯一可行的。
谢谢,我会尝试所有这些,并得到结果。 – GTCrais
已经尝试了您的建议并更新了结果。可悲的是,它没有奏效。 – GTCrais