V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MySQL 5.5 Community Server
MySQL 5.6 Community Server
Percona Configuration Wizard
XtraBackup 搭建主从复制
Great Sites on MySQL
Percona
MySQL Performance Blog
Severalnines
推荐管理工具
Sequel Pro
phpMyAdmin
推荐书目
MySQL Cookbook
MySQL 相关项目
MariaDB
Drizzle
参考文档
http://mysql-python.sourceforge.net/MySQLdb.html
abcbuzhiming
V2EX  ›  MySQL

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

  •  
  •   abcbuzhiming · 2018-11-07 12:37:59 +08:00 · 7540 次点击
    这是一个创建于 2207 天前的主题,其中的信息可能已经有所发展或是发生改变。
    以前从未意识到这个问题,我一直以为 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:如果关系数据库在写入上有这样的限制,我可不可以认为关系数据库实际是偏向“写少读多”场景的?
    10 条回复    2018-11-09 10:45:02 +08:00
    xiaoxinshiwo
        1
    xiaoxinshiwo  
       2018-11-07 16:12:47 +08:00
    发生死锁是因为并发时唯一索引冲突导致;没这个前提并不会出现 insert 死锁的情况吧
    gaius
        2
    gaius  
       2018-11-07 16:17:58 +08:00
    也就跳主键
    ziding
        3
    ziding  
       2018-11-07 16:20:47 +08:00
    MySQL 我不确定,但是一个正规的关系数据库,连这个冲突都处理不了那就太 SB 了。回到问题中:锁是可以升级的,就你的例子中,2 和 3 会有一个获得写锁,而不是两个互傻等。数据库层面的死锁一般是由于写操作顺序不一致导致的,比如:
    session1 update A -> update B
    ziding
        4
    ziding  
       2018-11-07 16:21:13 +08:00
    MySQL 我不确定,但是一个正规的关系数据库,连这个冲突都处理不了那就太 SB 了。回到问题中:锁是可以升级的,就你的例子中,2 和 3 会有一个获得写锁,而不是两个互傻等。数据库层面的死锁一般是由于写操作顺序不一致导致的,比如:
    session1 update A -> update B
    session2 update B -> update A
    abcbuzhiming
        5
    abcbuzhiming  
    OP
       2018-11-07 16:48:05 +08:00
    @xiaoxinshiwo 没错啊,就是有唯一索引导致,实际开发中唯一索引很常见
    @ziding MySQL 貌似就是处理不了,因为 1 个 session 要写和更新,需要获得排他锁,而排他锁和所有锁互斥,所以需要所有 session 都不持有锁,我说的案例里,两个 session 都持有共享锁,都去申请排他锁,他们都需要对方先释放掉共享锁,才能得到排他锁。所以就等在那死锁了。虽然说 MySQL 自己有死锁检测和处理,但是这个方式真的有点 2,而且 mysql 不认为这是 bug,它就是这么设计的
    noNOno
        6
    noNOno  
       2018-11-07 17:03:04 +08:00
    数据库本来就是写少读多呀.
    如果是做数据中转,读≈写 上消息队列 比如 kafka.
    xiaoxinshiwo
        7
    xiaoxinshiwo  
       2018-11-07 17:23:44 +08:00
    @abcbuzhiming #5 我的意思是唯一索引其实在业务中冲突的概率很低的
    siroccoicode
        8
    siroccoicode  
       2018-11-07 18:09:03 +08:00
    唯一索引在业务中很常见,但是需要这样高并发地 insert、delete 具有唯一索引的业务场景不是很多。如果有,应该重新考虑这块表的设计了。我觉得这可能是需求 /场景发生概率和实现权衡取舍的结果。
    glacer
        9
    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
        10
    ziding  
       2018-11-09 10:45:02 +08:00
    @abcbuzhiming 还好我一直不吊 MySQL,这个实现不是 BUG,我也是醉了。也有可能 MySQL 的死锁检测算法比其他的 DB 都 NB,所以依赖死锁检测 :P
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2765 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:33 · PVG 21:33 · LAX 05:33 · JFK 08:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.