MySQL5.7-回滚时死锁,但在重复键上使用时提交时死锁

kt06eoxx  于 2021-06-20  发布在  Mysql
关注(0)|答案(1)|浏览(418)

背景:我有一个有许多并发线程的应用程序。有一点,我想更新某个数据库行,但我不能确定该行是否确实存在。因此,我需要创建行或更新行(如果存在)。但是这样做,我遇到了我在这里提出的问题。
在使用mysql 5.7时,我遇到了一系列导致死锁的事件,但我真的不明白为什么。使用三种不同的客户端( c1 , c2 以及 c3 ),我们执行以下查询链:

c1 > BEGIN;
c1 > INSERT INTO `user_points` (userid, points) VALUES (1,1) 
     ON DUPLICATE KEY UPDATE points = points + 1;

c2 > INSERT INTO `user_points` (userid, points) VALUES (1,1) 
     ON DUPLICATE KEY UPDATE points = points + 1;

c3 > INSERT INTO `user_points` (userid, points) VALUES (1,1) 
     ON DUPLICATE KEY UPDATE points = points + 1;

c1 > ROLLBACK;

这一系列查询将导致 c3 接收到死锁错误,而 c2 执行查询时没有错误。但是,如果最后一个查询 c1 是一个提交(而不是回滚),那么这个系列就可以正常工作了。
以下是我使用的表格:

CREATE TABLE `points` (
    `userid` int(11) unsigned NOT NULL,
    `points` int(11) unsigned NOT NULL,
PRIMARY KEY (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

以下是innodb引擎报告的死锁:

------------------------
LATEST DETECTED DEADLOCK
------------------------
180802  8:48:02

***(1) TRANSACTION:

TRANSACTION 1D69A6, ACTIVE 3 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1248, 2 row lock(s)
MySQL thread id 135, OS thread handle 0x7f3c9829b700, query id 12166 172.30.0.1 root update
INSERT INTO points (userid, points) VALUES (1,0) ON DUPLICATE KEY UPDATE points = points + 1

***(1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 0 page no 55819 n bits 72 index `PRIMARY` of table `temp`.`points` trx id 1D69A6 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

***(2) TRANSACTION:

TRANSACTION 1D69A7, ACTIVE 1 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1248, 2 row lock(s)
MySQL thread id 137, OS thread handle 0x7f3c98239700, query id 12167 172.30.0.1 root update
INSERT INTO points (userid, points) VALUES (1,0) ON DUPLICATE KEY UPDATE points = points + 1

***(2) HOLDS THE LOCK(S):

RECORD LOCKS space id 0 page no 55819 n bits 72 index `PRIMARY` of table `temp`.`points` trx id 1D69A7 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

***(2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 0 page no 55819 n bits 72 index `PRIMARY` of table `temp`.`points` trx id 1D69A7 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

***WE ROLL BACK TRANSACTION (2)

我在google上搜索了一下,发现了一个与我类似的问题:为什么commit不会导致死锁。但不会使用 ON DUPLICATE KEY 回避那个问题中描述的问题?在mysql参考页中,我阅读了以下内容:
插入。。。重复密钥更新与简单插入的不同之处在于,当发生重复密钥错误时,将在要更新的行上放置独占锁而不是共享锁。对重复的主键值采用独占索引记录锁。对重复的唯一键值采用独占的下一个键锁。
我想做的是一个非常基本的操作:如果行不存在,就创建它;如果行已经存在,就更新它。怎么会不起作用呢?这到底是怎么回事?为什么我在回滚时收到死锁,而不是在提交时收到死锁?

gt0wga4j

gt0wga4j1#

innodb目标
数据的完整性。
快。
最小化边缘情况下的假死锁等。
你会放弃1吗?我怀疑。
你能放松一下吗?也许 吧。但这不是边缘案的决定。
请随意在bugs.mysql.com上提交一个bug;也许有一种修复方法既不会损害#1,也不会让事情“太”慢。

相关问题