背景:我有一个有许多并发线程的应用程序。有一点,我想更新某个数据库行,但我不能确定该行是否确实存在。因此,我需要创建行或更新行(如果存在)。但是这样做,我遇到了我在这里提出的问题。
在使用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参考页中,我阅读了以下内容:
插入。。。重复密钥更新与简单插入的不同之处在于,当发生重复密钥错误时,将在要更新的行上放置独占锁而不是共享锁。对重复的主键值采用独占索引记录锁。对重复的唯一键值采用独占的下一个键锁。
我想做的是一个非常基本的操作:如果行不存在,就创建它;如果行已经存在,就更新它。怎么会不起作用呢?这到底是怎么回事?为什么我在回滚时收到死锁,而不是在提交时收到死锁?
1条答案
按热度按时间gt0wga4j1#
innodb目标
数据的完整性。
快。
最小化边缘情况下的假死锁等。
你会放弃1吗?我怀疑。
你能放松一下吗?也许 吧。但这不是边缘案的决定。
请随意在bugs.mysql.com上提交一个bug;也许有一种修复方法既不会损害#1,也不会让事情“太”慢。