2016-11-22 48 views
1

我有表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 WRITEREAD ONLY。所有3个选项都需要设置PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true);WITH CONSISTENT SNAPSHOTREAD WRITE仍然给我造成了死锁错误,而READ ONLY自然也没有让我执行INSERT

所以现在看来​​手动锁定表是唯一可行的。

回答

0

我不知道这是否会帮助,但有一个交易周围的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声明中。请参阅here

+0

谢谢,我会尝试所有这些,并得到结果。 – GTCrais

+0

已经尝试了您的建议并更新了结果。可悲的是,它没有奏效。 – GTCrais