全局锁

  • 对整个数据库实例加锁
  • 加锁命令:flush tables with read lock,使整个数据库处于只读状态
    • unlock tables:释放锁
    • 异常自动释放
    • 同等效果:set global readonly=true
      • 此参数有时候会用来判断是主库还是备库
  • 使用场景:全库逻辑备份

    • InnoDB引擎的库备份:
      • mysqldump -single-trancaction
      • 导出线程使用可重复读隔离级别
      • 启动一个事务,拿到一致性视图

        表级锁

        表锁

  • 加锁:lock tables …read/write

  • unlock tables主动释放锁;客户端断开自动释放

    元数据锁——MDL(Meta Data Lock)

  • 当对一个表做增删改查操作时候,会加MDL读锁

  • 当对表结构变更操作时候,加上MDL写锁
  • 读锁之间不互斥
  • 读锁与写锁、写锁与写锁互斥

    行锁

  • 引擎层实现

  • InnoDB 支持行锁,MYSIAM不支持
  • 锁是锁的索引,不是数据
  • RC和RR的加锁基本一致,不同的是RR为了防止幻读会额外加上间隙锁。

    两阶段锁协议:

  • 在InnoDB事务中,行锁是在需要的时候加上,但并不是不需要了就立刻释 放,而是要等到事务结束时才释放

  • 如果你的事务中需要锁多个行,要把 最可能造成锁冲突、最可能影响并发度的锁尽量往后放

    死锁和死锁检测

  • 死锁原因:当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致 这几个线程都进入无限等待的状态,称为死锁

  • 死锁处理策略

    1. 直接进入等待,直到超时。innodb_lock_wait_timeout来设置超时时间(默认50s)
    2. 发起死锁检测。主动回滚死锁链条中的某一个事务,让其他事 务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑
      • 热点 行更新时,会扫描检查很多的线程,消耗大量CPU资源
        • 解决方法:控制并发度、将一行数据改成逻辑上的多行减少锁冲突

          加锁机制

  • InnoDB对于每个符合条件的记录是分步加锁的,即先加二级索引再加主键索引;其次是按记录逐条加锁的,即加完一条记录后,再加另外一条记录,直到所有符合条件的记录都加完锁

  • 锁是加在索引上

    根据主键进行更新

  • update t set name=’xxx’ where id=29;只需要将主键上id=29的记录加上X锁即可(X锁称为互斥锁,加锁后本事务可以读和写,其他事务读和写会被阻塞)

  • 主键加锁.jpg

    根据唯一索引进行更新

  • update t set name=’xxx’ where name=’ddd’;这里假设name是唯一的。InnoDB现在name索引上找到name=’ddd’的索引项(id=29)并加上加上X锁,然后根据id=29再到主键索引上找到对应的叶子节点并加上X锁。

  • 一共两把锁,一把加在唯一索引上,一把加在主键索引上。这里需要说明的是加锁是一步步加的,不会同时给唯一索引和主键索引加锁。这种分步加锁的机制实际上也是导致死锁的诱因之一
  • 唯一索引jiasuo.jpg

    根据非唯一索引进行更新

  • update t set name=’xxx’ where name=’ddd’;这里假设name不唯一,即根据name可以查到多条记录(id不同)。和上面唯一索引加锁类似,不同的是会给所有符合条件的索引项加锁

  1. 在非唯一索引(name)上找到(ddd,29)的索引项,加上X锁;
  2. 根据(ddd,29)找到主键索引的(29,ddd)记录,加X锁;
  3. 在非唯一索引(name)上找到(ddd,37)的索引项,加上X锁;
  4. 根据(ddd,29)找到主键索引的(37,ddd)记录,加X锁;

非唯一索引加锁.jpg

加锁规则

两个原则

  • 加锁的基本单位是next-key lock(行锁+间隙锁、前开后闭)
  • 查询过程中访问到的对象才会加锁

    两个优化

  • 唯一索引的等值查询,next-key lock会退化为行锁

  • 索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-keylock退化为间隙锁。

    一个BUG

  • 唯一索引上的范围查询会访问到不满足条件的第一个值为止。