MySQL的InnoDB中的各种锁是怎么工作的

参考 https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html

本文尽量排除不必要的细节,只看锁和锁实现的功能,文章基于MySQL8.0的官方文档

以下内容描述基于可重复度事务隔离级别(Repeatable Read)

宏观上来看锁分表锁和行锁

当你把这些锁搞明白是干嘛的了, MySQL的事务隔离基本就能搞明白了


0. 预备知识

  • 为什么要有锁? 锁是保证 并发 和 数据一致性 的权衡的产物,也就是说有了锁,既保证了并发的能力,又保证了数据一致性
  • 脏读: 读到了别的事务回滚前的脏数据
  • 不可重复读 : 同一个事务中对同一行数据读取两次返回结果不一样
  • 幻读: 同一个事务中范围查询,第一次读N条,第二次读到N+M条
  • 锁可以解决脏读,不可重复读,脏读的问题,维护了事务的特性
  • 快照读是不会上锁的
  • 当前读会上锁, 当前读有两种
    1. select ... lock in share mode 会尝试获取共享锁
    1. select ... for update 会尝试获取排它锁
  • update,insert,delete 都会尝试获取排它锁
  • 排它锁共享锁是啥,看下文

1. 行级锁

这两种是标准的行锁(行级共享锁和排他锁)

1.1. 共享锁Shared Locks (S)

  • 用来给指定的行加锁,读取
  • 加锁方式: select ... lock in share mode
  • 多个共享锁,锁同一个行是不会互相冲突的,所以叫共享锁

1.2. 排他锁Exclusive Locks(X)

  • 加锁方式: insert,update,delete,select ... for update

1.3 共享锁和排它锁的冲突情况

  1. 一下是在同一行加锁的情况
  2. T1加上了共享锁 S,T2可以加共享锁 S但不能加排它锁 X
  3. T1加上了排它锁 X,T2不可以加共享锁 S排它锁 X
  4. 加不上锁,就只能被阻塞,等待T1释放锁

1.4 共享锁排他锁用途

主要是解决脏读,不可重复读和幻读的问题
共享锁和排他锁加锁有一下几种情况(锁再同一个行row上面):

  • 如果T1已经加了 S Lock ,那么T2在同一行上面可以加S Lock
  • 如果T1已经加了 S Lock ,那么T2在同一行上面不可以加X Lock,T2被阻塞
  • 如果T1已经加了 X Lock ,那么T2在同一行上面不能加S LockX Lock,T2被阻塞,T2只能等待T1释放锁

2. 表级意向锁

表级意向锁

Intention locks are table-level locks that indicate which type of lock (shared or exclusive) a transaction requires later for a row in a table.

  • 意向锁是表级别的锁, 它的存在是为了体现出是不是有某个事务对这个表上了行锁
  • 意向锁的主要目的是为了展示出,有某个事务正在锁住这个表的某行,节省了有些情况下其他事务直接去查找行锁的时间

2.1. 意向共享锁 Intention Shared Locks (IS)

  • 如果一个表有IS锁,则表明,现在有一个事务已经获得或者将要获得 共享锁 Shared Locks
  • SELECT ... FOR SHARE 可以设置S锁, 同时数据库帮我们触发IS(老版本是:select ... lock in share mode)
  • 如果一个事务想要获取 共享锁 S ,则必须先获取 意向共享锁 IS

2.2. 意向排他锁 Intention Exclusive Locks (IX)

  • 如果一个表有IX锁,则表明,现在有一个事务已经获得或者将要获得 排他锁 Exclusive Locks
  • SELECT ... FOR UPDATE 可以设置 X锁, 同时数据库帮我们触发IX
  • 如果一个事务想要获取 排他锁 X ,则必须先获取 意向排他锁 IX

3. 行锁和表锁的冲突和兼容

  • X: 行级排它锁
  • IX: 表级排它锁
  • S: 行级共享锁
  • IS : 表级共享锁

如果新的事务将要获取的锁 和已有的事务发生冲突,则一直阻塞到已有的事务释放锁
具体各个锁之间的关系看下表:

↓(T1)↓ \ →(T2)→XIXSIS
XConflictConflictConflictConflict
IXConflictCompatibleConflictCompatible
SConflictConflictCompatibleCompatible
ISConflictCompatibleCompatibleCompatible

4 行锁的实现方式

实现方式有: 记录锁,间隙锁,临键锁
他们与共享锁,排他锁相比是另外一种维度
也就是说,记录锁既可以是共享锁,又可以是排他锁,就看你怎么用

4.1 记录锁(Record Locks)

记录锁

  • 记录锁是锁在索引上的,锁的是具体的某个值,这个值正好在索引上
  • 如果表没有索引会被设置在聚簇索引上
  • 当where字句,精确匹配到索引上的某个值
    例: select a from t where a = 10,这里就会阻止insert,update,delete对a=10这行的操作
    (注:同时还会在10左右创造两个间隙锁,实际上RR隔离级别创造的是临键锁,看3.3的介绍)

4.2 间隙锁(Gap Locks)

间隙锁

  • 间隙锁是把锁加在两个索引值之间,比如索引只有两个值1,10,那么间隙锁就可能会在(1,10),(infimum,1),(10,supremum)这几个开区间上加锁(infimum是最小值,supremum 是最大值)
  • 间隙可以横跨多个索引的值,假如有索引值1,5,10,select * from t where a between 2 and 8 for update,就会锁住(1,5)(5,10)这2个开区间
  • 间隙锁会在扫描索引的时候,把条件所指范围扫到的间隙 gap都锁上,也就是说,可能会锁上多个间隙
  • 既然这些间隙被锁住了,那么想要在这些间隙上插入值,肯定会被阻塞的
  • 注意: 如果是唯一索引精确查询,则只会对查到的那一行加记录锁,不会加间隙锁

例:

  1. 设a有三个值1,10,20 ,a字段上有索引
  2. select a from t where a = 10 for update 会创造两个间隙锁``(1,10),(10,20)和一个记录锁 a = 10
  3. select a from t where a = 15 for update 会创造一个间隙锁 (10,20)
  4. (另: 如果where b = 10,b上没有索引,我发现全表都被锁住了,具体原理我还没看)

4.3 临键锁(Next-key Locks)

临键锁

  • 临键锁实际上就是记录锁间隙锁的组合
  • where条件所指范围,只要扫描到的记录锁间隙锁都组合起来,这个锁就叫临键锁
  • 临键锁解决了幻读的问题,这样只要第一次读取的where条件范围内都被锁住,那么第二次读取,也就能保证和第一次读取一致了.
  • 官方描述幻读问题

4.4 插入意向锁(Insert Intention Locks)

插入意向锁

  • 插入意向锁实际上是一种特别的间隙锁
  • 当insert操作发生时,会尝试在指定索引的间隙上加一个这个锁
  • 这个锁有个特点, 如果两个插入操作都加在同一个间隙gap上,而这个间隙gap之前没有其他种类的间隙锁,这两个插入操作,会都会成功设置插入意向锁,且不会互相阻塞的.
  • 插入意向锁解决了一个并发插入的问题,如果多个事务在同一个间隙上只发生插入操作,大家都去等待一个排它锁那么效率会很低,而如果使用插入意向锁则多个插入事务可以同时并发进行

4.5 自增锁(AUTO-INC Locks)

自增锁

  • 自增锁是一种特殊的表锁
  • 也就是说所有的insert操作都会排队等待这个自增锁来获取自增值
  • 但是这个锁的只会在insert操作的时候存在,与事务无关
  • 一旦insert操作结束,锁就会释放
  • 自增锁设计目的是为了保证自增值获取的顺序性
  • 一般这个自增锁是不会影响性能的,所以不用太多考虑
  • 具体的自增算法看官网,这里不赘述

4.6 空间索引上的谓词锁

5. 总结

MySQL这么多锁,实际上就是在为一个目的服务: 在保证数据一致性的前提下,提升并发性能

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值