MySQL 死锁问题:它实际只能“并发读,单线程写入”?

2018-11-07 12:37:59 +08:00
 abcbuzhiming
以前从未意识到这个问题,我一直以为 MySQL 在多线程数据同步问题上不需要使用者过多的考虑,内部有那么多机制能保证多线程下数据一致问题。最近在深挖 MySQL 的锁知识的时候,发现了这一段,官网描述:
https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html

1 个表,存在唯一索引(主键索引也算唯一索引)。先让 1 个 session 去插入唯一索引所在列,然后 hold 在那里即不回滚,也不提交。此时另外启动两个 session,也插入同样的列值;这两个 session 为了检测唯一索引是否已经存在,会去申请共享锁,但由于此时第一个 session 持有互斥锁,会让后两个 session 申请共享锁的行为阻塞。2 和 3 的 session 就会等在那里;此时 1 的 session 回滚,释放掉互斥锁,2 和 3 的 session 会同时获得共享锁;他们要进一步获得互斥锁才能进行插入,但是,因为互斥锁和共享锁是冲突的,会形成,2 为了得到互斥锁必须等待 3 释放共享锁和 3 为了得到互斥锁也必须等待 2 释放共享锁的死锁现象,直到死锁超时回滚

我随便搜了一下发现这种死锁其实非常常见,网上有不少,在高一点的并发下 insert 就会出现死锁的情况。从原理上说,不需要 3 个 session,理论上,只要两个线程只要同时拿到共享锁就一定会发生死锁,

然而 mysql 官方并不认为这是个 bug。而我查到的解决办法居然是:在程序层上并发写变队列写(单线程写)。我总觉得这有点不合乎道理,有没有人聊聊其它家的数据库有解决方案吗,MySQL 的解决方法真的只有并发写变队列写这一条路了?

PS:如果关系数据库在写入上有这样的限制,我可不可以认为关系数据库实际是偏向“写少读多”场景的?
7522 次点击
所在节点    MySQL
10 条回复
xiaoxinshiwo
2018-11-07 16:12:47 +08:00
发生死锁是因为并发时唯一索引冲突导致;没这个前提并不会出现 insert 死锁的情况吧
gaius
2018-11-07 16:17:58 +08:00
也就跳主键
ziding
2018-11-07 16:20:47 +08:00
MySQL 我不确定,但是一个正规的关系数据库,连这个冲突都处理不了那就太 SB 了。回到问题中:锁是可以升级的,就你的例子中,2 和 3 会有一个获得写锁,而不是两个互傻等。数据库层面的死锁一般是由于写操作顺序不一致导致的,比如:
session1 update A -> update B
ziding
2018-11-07 16:21:13 +08:00
MySQL 我不确定,但是一个正规的关系数据库,连这个冲突都处理不了那就太 SB 了。回到问题中:锁是可以升级的,就你的例子中,2 和 3 会有一个获得写锁,而不是两个互傻等。数据库层面的死锁一般是由于写操作顺序不一致导致的,比如:
session1 update A -> update B
session2 update B -> update A
abcbuzhiming
2018-11-07 16:48:05 +08:00
@xiaoxinshiwo 没错啊,就是有唯一索引导致,实际开发中唯一索引很常见
@ziding MySQL 貌似就是处理不了,因为 1 个 session 要写和更新,需要获得排他锁,而排他锁和所有锁互斥,所以需要所有 session 都不持有锁,我说的案例里,两个 session 都持有共享锁,都去申请排他锁,他们都需要对方先释放掉共享锁,才能得到排他锁。所以就等在那死锁了。虽然说 MySQL 自己有死锁检测和处理,但是这个方式真的有点 2,而且 mysql 不认为这是 bug,它就是这么设计的
noNOno
2018-11-07 17:03:04 +08:00
数据库本来就是写少读多呀.
如果是做数据中转,读≈写 上消息队列 比如 kafka.
xiaoxinshiwo
2018-11-07 17:23:44 +08:00
@abcbuzhiming #5 我的意思是唯一索引其实在业务中冲突的概率很低的
siroccoicode
2018-11-07 18:09:03 +08:00
唯一索引在业务中很常见,但是需要这样高并发地 insert、delete 具有唯一索引的业务场景不是很多。如果有,应该重新考虑这块表的设计了。我觉得这可能是需求 /场景发生概率和实现权衡取舍的结果。
glacer
2018-11-07 18:45:47 +08:00
对于你提到的场景,MySQL 内部有死锁检测机制能检测出事务 2 和 3 的死锁情况,并将已执行语句最少的事务回滚,所以这种死锁情况对性能影响应该是比较小的。https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlock-detection.html
ziding
2018-11-09 10:45:02 +08:00
@abcbuzhiming 还好我一直不吊 MySQL,这个实现不是 BUG,我也是醉了。也有可能 MySQL 的死锁检测算法比其他的 DB 都 NB,所以依赖死锁检测 :P

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/505341

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX