zoukankan      html  css  js  c++  java
  • 实战演示疑惑 mysql insert到底加什么锁

    innodb的事务隔离级别是可重复读级别且innodb_locks_unsafe_for_binlog禁用,也就是说允许next-key lock

     实验来自网上. ( 如果你没有演示出来,请check order_id 是否是非unique key.) 如果你看不懂,请看后续文章. next-key lock (glap lock)完全解析.

    CREATE TABLE `LockTest` (

       `order_id` varchar(20) NOT NULL,
       `id` bigint(20) NOT NULL AUTO_INCREMENT,
       PRIMARY KEY (`id`),
       KEY `idx_order_id` (`order_id`)
     ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8

     select * from LockTest;

     empty set;

    事务1 事务2
    begin

    delete from LockTest where order_id =  'D20'

     
     
     

    begin
    delete from LockTest where order_id =  'D19'

    insert into LockTest (order_id) values ('D20')

     
     
    insert into LockTest (order_id) values ('D19')

    commit
    commit

    事务1 执行到insert语句会block住,事务2执行insert语句会提示死锁错误

    错误码: 1213
    Deadlock found when trying to get lock; try restarting transaction

    Execution Time : 00:00:00:000
    Transfer Time : 00:00:00:000
    Total Time : 00:00:00:000

     show engine innodb status 显示死锁信息

    ------------------------
    LATEST DETECTED DEADLOCK
    ------------------------
    2014-04-30 15:01:55 a233b90
    *** (1) TRANSACTION:
    TRANSACTION 596042, ACTIVE 7 sec inserting
    mysql tables in use 1, locked 1
    LOCK WAIT 3 lock struct(s), heap size 320, 2 row lock(s), undo log entries 1
    MySQL thread id 10851, OS thread handle 0x2abfb90, query id 251521 10.10.53.122 root update
    insert into LockTest (order_id) values ('D20')
    *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 502 page no 4 n bits 72 index `idx_order_id` of table `test`.`LockTest` trx id 596042 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 596041, ACTIVE 19 sec inserting
    mysql tables in use 1, locked 1
    3 lock struct(s), heap size 320, 2 row lock(s), undo log entries 1
    MySQL thread id 10848, OS thread handle 0xa233b90, query id 251522 10.10.53.122 root update
    insert into LockTest (order_id) values ('D19')
    *** (2) HOLDS THE LOCK(S):
    RECORD LOCKS space id 502 page no 4 n bits 72 index `idx_order_id` of table `test`.`LockTest` trx id 596041 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 502 page no 4 n bits 72 index `idx_order_id` of table `test`.`LockTest` trx id 596041 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)

    简单分析上面的场景先删除再插入的sql是hibernage保存集合关联的处理方式。delete语句删除不存在且删除的order_id大于现有表中的所有order_id,所以delete语句会使用next-key锁住(当前最大-无穷大) 

    lock_id lock_trx_id lock_mode lock_type lock_table lock_index lock_space lock_page lock_rec lock_data
    596133:502:4:1 596133 X RECORD `test`.`LockTest` idx_order_id 502 4 1 supremum pseudo-record
    596134:502:4:1 596134 X RECORD `test`.`LockTest` idx_order_id 502 4 1 supremum pseudo-record
     

    比较奇怪的是为啥两个事务都拿到了相同区间的(当前最大-无穷大)的X锁。不过换成read-commited级别后就没死锁了。

    终于在官方文档找到答案, 区间锁只是用来防止其他事务在区间中插入数据,区间x锁 与区间S锁效果是一样的。也就是说不会因为两个事务都用加相同区间锁而相互等待的

    https://dev.mysql.com/doc/refman/5.1/en/innodb-record-level-locks.html

    Gap locks in InnoDB are “purely inhibitive”, which means they only stop other transactions from inserting to the gap. Thus, a gap X-lock has the same effect as a gap S-lock.

    当两个事务拿到相同区间锁后,就会阻止对方忘区间内做insert操作。所以第一个事务insert会阻塞,第二个事务会提示死锁

    详情见 https://dev.mysql.com/doc/refman/5.6/en/innodb-record-level-locks.html

    换成read-commited级别后就没死锁了! 因为没有了间隙所,读提交不需要幻读控制,也就不需要间隙锁了.

    万变归一: 事务内加锁总归是为了隔离级别.

    再来分析另外一个现象:

        上述现象表明, delete/update会阻塞insert .那么换成先insert,再delete/update呢?

        实验表明不会阻塞?  这个感觉挺矛盾的,锁的互斥是相对的. 主要原因是insert 不会产生 间隙锁.

    间隙锁的作用本身就是单向的.

    再次从事务内加锁原因是为了隔离级别这个角度分析.  insert 

    【解决方案有两种】
    1、改变程序中数据库操作的逻辑
    2、取消gap lock机制 
    innodb_locks_unsafe_for_binlog启用

    或者设置为隔离级别为读提交
    Gap locking can be disabled explicitly.This occurs if you change the transaction isolation level to READ COMMITTED orenable the innodb_locks_unsafe_for_binlog system variable.
    3. 加上unique锁


    select for update / update  where 

    1. 有该行  对非unique列会加 间隙共享锁 和 行锁 见 (14.2.2.4 InnoDB Record, Gap, and Next-Key Locks http://dev.mysql.com/doc/refman/5.7/en/innodb-record-level-locks.html)

    2. 无该行 对非unique  会加间隙共享锁  . 这个文档比较麻烦. 要通过 上面(该文)

       (Unexpected deadlock between concurrent INSERTs when unique key violation may occ http://bugs.mysql.com/bug.php?id=35821)和

       (Deadlock detected on concurrent insert into same table (InnoDB) https://bugs.mysql.com/bug.php?id=43210 )

    里面有个很好玩的案例,这两个中文博客里也是该话题 (innodb next-key lock引发的死锁 http://www.cnblogs.com/xhan/p/3701459.html)
    ---------------------
    原文:https://blog.csdn.net/fei33423/article/details/46731891

  • 相关阅读:
    mysql索引
    springboot mybatis 后台框架平台 shiro 权限 集成代码生成器
    java 企业网站源码模版 有前后台 springmvc SSM 生成静态化
    java springMVC SSM 操作日志 4级别联动 文件管理 头像编辑 shiro redis
    activiti工作流的web流程设计器整合视频教程 SSM和独立部署
    .Net Core中的ObjectPool
    文件操作、流相关类梳理
    .Net Core中的配置文件源码解析
    .Net Core中依赖注入服务使用总结
    消息中间件RabbitMQ(一)
  • 原文地址:https://www.cnblogs.com/DataArt/p/10177905.html
Copyright © 2011-2022 走看看