zoukankan      html  css  js  c++  java
  • mysql5.7的锁:乐观锁/共享锁、互斥/排他锁、意向锁、记录锁、行锁/表锁、间隙锁、临界锁、插入意向锁、自增锁、空间索引预测锁、隐式锁

    从锁的模式来区分:Shared Locks共享锁(行锁)、Exclusive Locks(行锁)

    意向锁区分: Intention Shared Locks意向共享锁、Intention Exclusive Locks意向排它锁

    从锁的算法区分:Record Locks记录锁、Gap Locks间隙锁、Next-Key Locks临界锁

    一致性读/快照读

    【锁定读(Locking Reads)/LBCC】

    当前读分类:select for update、select .... lock in share mode、update insert delete、串行化的级别,当前读的这种方式就称为LBCC,全称为 lock-based Concurreny Control 基于锁的并发控制。

    【共享锁和独占锁】

    共享锁,简称S锁,其他线程可以继续获取共享锁。

    独占锁,简称X锁,只允许获取一次,不容许其他线程获取独占锁。所以像select for update就会独占线程锁死这行。

    insert    一般不加锁,隐式锁

    update    X锁:   不更新主键的情况下,就地更新,更新主键的情况下。

                   X锁:   更新主键的情况下,先执行delete操作,  (隐式锁:)再执行insert操作。

    delete   X锁

    组合型 X 独占锁 IX 意向独占锁 S 共享锁 IS 意向共享锁
    表锁
    行锁 -- --

    表锁级别的兼容性,比如X独占锁与X独占锁就不兼容:

    兼容性 X 独占锁 IX 意向独占锁 S 共享锁 IS 意向共享锁
    X 独占锁 不兼容 不兼容 不兼容 不兼容
    IX  意向独占锁 不兼容 兼容 不兼容 兼容
    S 共享锁 不兼容 不兼容 兼容 兼容
    IS 意向共享锁 不兼容 兼容 兼容 兼容

    怎么给表加锁?

    Lock table t Read;  表级别S锁

    Lock table t Write;  表级别X锁

    而innodb已经有行锁了,所以以上这种手动加锁没太大意义,除非崩溃时恢复数据的特殊时期。

    InnoDB中的表级锁

    1、表级别的S锁、X锁、IS锁、IX锁、

    2、元数据锁(Metadata Locks, 简称MDL)Server

    3、AUTO-INC锁,自增主键锁,解决主键自增列避免产生重复自增主键的锁

    InnoDB中的行级锁

    1、Record Locks(官方名称是:Lock_Rec_Not_Gap)   记录锁

    2、Gap Locks(官方名称:Lock_Gap) 间隙锁,对某条记录的前后,也就是两条记录之间的间隙加锁,不对行本身加锁。

    3、Next-Key Locks(官方名称:Lock_ORDINARY) 临界锁

    4、Insert Intention Locks  插入意向锁

    5、隐式锁

    【间隙锁例子】

    假设我在teacher表中为name这一列建立了一个索引idx_name:

    事务1 事务2

    select * from teacher order by name;     --  按照idx_name列的索引排序,查出来的数据是这样的:

     
    begin;     -- 开启事务  
    update teacher set domain = 'Spring' where name = 'James';   -- 这行id是9,此时事务尚未提交,mysql会对id=9的行前后都加间隙锁,不允许此行前后插入数据。  
      begin;   -- 开启事务
      insert into teacher values(22, 'Jahes', 'docker');  -- 在name='James'行的后面添加新行,此sql会阻塞住, insert失败,因为事务1的间隙锁已开启。如果长时间阻塞住(也就是事务1长时间不提交),会报错:ERROR 1205(HY000): Lock wait timeout exceeded; try restarting trasaction;
      inset into teacher values(7, 'King', 'docker');  -- 根据idx_name索引列的B+tree排序规则,由于King值已存在,所以需要将同name值的id进行排序,此insert语句中的id 7 要排到id=15的前面去,刚好落在间隙锁范围,同样阻塞住,insert失败
      insert into teacher values(70,'King','docker');  -- 此时id 70落在id=15的后面,避开了间隙锁,insert成功
       

    【临界锁例子】

    临界锁就是记录锁和间隙锁的合体,也就是不仅像记录锁一样锁住行本身,还像间隙锁一样锁住了行的前后间隙,不允许insert。

    那么可重复读的事务隔离级别下,innodb引擎就是使用了临界锁进行搜索和索引扫描,来避免幻读的情况。

    【插入意向锁例子】

    根据间隙锁的例子,在事务2中insert数据需要别的事务进行提交或回滚才允许事务2insert的情况,那么开启一个插入意向锁,类似于排队机制。

    【隐式锁例子】

    一个事务可以不显式的去加锁,但是别的事务在对新加的记录准备加X或S锁的时候,首先会去检查trx_id这个隐藏列,然后进行相关的锁和事务判断(这里参考下面的锁的内存结构)。

    假如A事务先insert一条新记录,然后B事务去对这个新记录进行S或X锁的时候,B事务会帮助A事务在内存中生成锁结构,B事务自己再生成一个锁结构,然后把自己置为等待状态。

    隐式锁的好处是延迟锁的生成,如果一个insert语句是不需要锁的,但是如果多条sql插入相同id,那么隐式锁的存在就发挥了作用,就像并发编程里的偏向锁升级成了轻量锁。

    【锁的内存结构】

    每条数据记录的锁结构都包含两个信息:事务信息 = t1;  是否等待 = false;  false表示该锁是当前事务创建的,当前事务对获取该锁不需要等待。

    当事务1提交之后,事务1会去唤醒等待该行数据记录的其他锁,相应的其他锁结构也包括两个信息:事务信息 = 2;是否等待 = true;  依次唤醒所有锁和事务。

    如果是独占锁,那么一次唤醒一个,如果是共享锁,那么一次可以唤醒多个。

    同一个事务,针对同一个数据页面,同一个加锁类型,才会公用一把锁,这三者只要任何一个不同,都会生成一把新的锁,但是锁结构相同。

    【锁的开启和查看】

    show variables like 'innodb_status_output_locks';    -- 默认是OFF关闭状态,需要开启: set global innodb_status_output_locks = ON;   

    show engine innodb statusG    -- 查看innodb加锁的情况就会变得非常非常详细:

    如果你的sql中加锁很慢的情况,就可以用这种方法来判断事务之间是否出现了严重的锁争抢情况。

    【死锁】

    一般来说只要有并发和死锁的共同加持情况,都会有死锁的身影,两个或两个以上的进程在执行过程中,由于竞争资源或者通讯问题造成彼此阻塞现象,若无外力作用而无法推进解决,此时称为死锁或者死锁状态。

    产生死锁的必要条件:

    1)互斥。进程对于分配给他的资源是排他性使用。

    2)请求和保持。我已经通过请求拿到了一个资源,但是我又提出了一个新资源的请求 ,但是更多的资源我拿不到,同时对于我自己已有的资源也不放手,所以这叫请求与保持。

    3)不剥夺。对于进程已经持有的资源在使用完之前不剥夺。

    4)环路等待。

    死锁例子:

    事务1 事务2
    begin;    -- 开启一个事务  
    select * from teacher where number = 1 for update;   -- 加了一个行上的X锁  
      begin;
      select * from teacher where number = 3 for update;  -- 加了一个行上的X锁
    select * from teacher where number = 3 for update;  -- 此时会阻塞,因为锁被事务2抢走
     

    select * from teacher where number = 1 for update;   -- 尝试加锁,但是失败:发生死锁,会报错:

    ERROR 1213 (40001) : Deadlock found when trying to get lock; try restarting trasaction

    发生死锁之后,mysql自动介入结束当前事务

    此时上面的 3 for update会执行下去了,不再阻塞,因为事务2已经结束了。  
    commit;        -- 此时rollback 或者 commit都可以结束事务。  
    show engine innodb statusG    -- 此时将会看到最近一个死锁产生的情况  

    end.

    支付宝扫一扫,为女程序员打赏!
    作者:梦幻朵颜
    版权:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    移动端开发适配总结
    gulp进阶构建项目由浅入深
    css3 实现逐帧动画
    jQuery1.9.1源码分析--数据缓存Data模块
    nodejs开发指南读后感
    css3动画由浅入深总结
    浅谈javascript函数节流
    go语言基础之copy的使用
    go语言基础之append扩容特点
    go语言基础之append函数的使用
  • 原文地址:https://www.cnblogs.com/zhuwenjoyce/p/15060318.html
Copyright © 2011-2022 走看看