MySQL - 尝试锁定

时间:2016-11-22 03:06:34

标签: mysql deadlock

我的表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
)

为了测试,我同时插入了大量用户,每次遇到死锁错误。

我看了这篇帖子 - How to avoid mysql 'Deadlock found when trying to get lock; try restarting transaction'但是我不明白我应该怎么做我的查询以防止死锁,如果可能的话,那么这个问题的答案就是问题 - {{3}无论如何,死锁都会发生。

我设法让这个工作,并且始终如一地工作的唯一方法是(语言是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%确定地防止死锁而无需手动锁定表格?

更新

Per @ wallyk的回答,我尝试了以下内容:

1)将查询放入事务中 - 仍然是死锁错误
2)使用WITH CONSISTENT SNAPSHOTREAD WRITEREAD ONLY开始交易。所有3个选项都需要设置PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true);WITH CONSISTENT SNAPSHOTREAD WRITE仍然给了我死锁错误,而READ ONLY自然甚至不允许我执行INSERT

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

1 个答案:

答案 0 :(得分: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