› 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 · Nov 7, 2018 · 8604 views
    This topic created in 2884 days ago, the information mentioned may be changed or developed.
    以前从未意识到这个问题,我一直以为 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 replies  •  2018-11-09 10:45:02 +08:00
    xiaoxinshiwo
        1
    xiaoxinshiwo  
       Nov 7, 2018
    发生死锁是因为并发时唯一索引冲突导致;没这个前提并不会出现 insert 死锁的情况吧
    gaius
        2
    gaius  
       Nov 7, 2018
    也就跳主键
    ziding
        3
    ziding  
       Nov 7, 2018
    MySQL 我不确定,但是一个正规的关系数据库,连这个冲突都处理不了那就太 SB 了。回到问题中:锁是可以升级的,就你的例子中,2 和 3 会有一个获得写锁,而不是两个互傻等。数据库层面的死锁一般是由于写操作顺序不一致导致的,比如:
    session1 update A -> update B
    ziding
        4
    ziding  
       Nov 7, 2018
    MySQL 我不确定,但是一个正规的关系数据库,连这个冲突都处理不了那就太 SB 了。回到问题中:锁是可以升级的,就你的例子中,2 和 3 会有一个获得写锁,而不是两个互傻等。数据库层面的死锁一般是由于写操作顺序不一致导致的,比如:
    session1 update A -> update B
    session2 update B -> update A
    abcbuzhiming
        5
    abcbuzhiming  
    OP
       Nov 7, 2018
    @xiaoxinshiwo 没错啊,就是有唯一索引导致,实际开发中唯一索引很常见
    @ziding MySQL 貌似就是处理不了,因为 1 个 session 要写和更新,需要获得排他锁,而排他锁和所有锁互斥,所以需要所有 session 都不持有锁,我说的案例里,两个 session 都持有共享锁,都去申请排他锁,他们都需要对方先释放掉共享锁,才能得到排他锁。所以就等在那死锁了。虽然说 MySQL 自己有死锁检测和处理,但是这个方式真的有点 2,而且 mysql 不认为这是 bug,它就是这么设计的
    noNOno
        6
    noNOno  
       Nov 7, 2018
    数据库本来就是写少读多呀.
    如果是做数据中转,读≈写 上消息队列 比如 kafka.
    xiaoxinshiwo
        7
    xiaoxinshiwo  
       Nov 7, 2018
    @abcbuzhiming #5 我的意思是唯一索引其实在业务中冲突的概率很低的
    siroccoicode
        8
    siroccoicode  
       Nov 7, 2018
    唯一索引在业务中很常见,但是需要这样高并发地 insert、delete 具有唯一索引的业务场景不是很多。如果有,应该重新考虑这块表的设计了。我觉得这可能是需求 /场景发生概率和实现权衡取舍的结果。
    glacer
        9
    glacer  
       Nov 7, 2018
    对于你提到的场景,MySQL 内部有死锁检测机制能检测出事务 2 和 3 的死锁情况,并将已执行语句最少的事务回滚,所以这种死锁情况对性能影响应该是比较小的。https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlock-detection.html
    ziding
        10
    ziding  
       Nov 9, 2018
    @abcbuzhiming 还好我一直不吊 MySQL,这个实现不是 BUG,我也是醉了。也有可能 MySQL 的死锁检测算法比其他的 DB 都 NB,所以依赖死锁检测 :P
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   813 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 21:55 · PVG 05:55 · LAX 14:55 · JFK 17:55
    ♥ Do have faith in what you're doing.