把MySQL中的各种锁及其原理都画出来( 四 )

其他类型的锁的规则较为简单:

  • 间隙锁不和其他锁(不包括插入意向锁)冲突;
  • 记录锁和记录锁冲突 , Next-key 锁和 Next-key 锁冲突 , 记录锁和 Next-key 锁冲突;
常见加锁场景分析今天我们就从原理走向实战 , 分析常见 SQL 语句的加锁场景 。了解了这几种场景 , 相信小伙伴们也能举一反三 , 灵活地分析真实开发过程中遇到的加锁问题 。
如下图所示 , 数据库的隔离等级 , SQL 语句和当前数据库数据会共同影响该条 SQL 执行时数据库生成的锁模式 , 锁类型和锁数量 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
下面 , 我们会首先讲解一下隔离等级、不同 SQL 语句 和 当前数据库数据对生成锁影响的基本规则 , 然后再依次具体 SQL 的加锁场景 。
隔离等级对加锁的影响MySQL 的隔离等级对加锁有影响 , 所以在分析具体加锁场景时 , 首先要确定当前的隔离等级 。
  • 读未提交(Read Uncommitted 后续简称 RU):可以读到未提交的读 , 基本上不会使用该隔离等级 , 所以暂时忽略 。
  • 读已提交(Read Committed 后续简称 RC):存在幻读问题 , 对当前读获取的数据加记录锁 。
  • 可重复读(Repeatable Read 后续简称 RR):不存在幻读问题 , 对当前读获取的数据加记录锁 , 同时对涉及的范围加间隙锁 , 防止新的数据插入 , 导致幻读 。
  • 序列化(Serializable):从 MVCC 并发控制退化到基于锁的并发控制 , 不存在快照读 , 都是当前读 , 并发效率急剧下降 , 不建议使用 。
这里说明一下 , RC 总是读取记录的最新版本 , 而 RR 是读取该记录事务开始时的那个版本 , 虽然这两种读取的版本不同 , 但是都是快照数据 , 并不会被写操作阻塞 , 所以这种读操作称为 快照读(Snapshot Read)
MySQL 还提供了另一种读取方式叫当前读(Current Read) , 它读的不再是数据的快照版本 , 而是数据的最新版本 , 并会对数据加锁 , 根据语句和加锁的不同 , 又分成三种情况:
  • SELECT ... LOCK IN SHARE MODE:加共享(S)锁
  • SELECT ... FOR UPDATE:加排他(X)锁
  • INSERT / UPDATE / DELETE:加排他(X)锁
当前读在 RR 和 RC 两种隔离级别下的实现也是不一样的:RC 只加记录锁 , RR 除了加记录锁 , 还会加间隙锁 , 用于解决幻读问题 。
不同 SQL 语句对加锁的影响不同的 SQL 语句当然会加不同的锁 , 总结起来主要分为五种情况:
  • SELECT ... 语句正常情况下为快照读 , 不加锁;
  • SELECT ... LOCK IN SHARE MODE 语句为当前读 , 加 S 锁;
  • SELECT ... FOR UPDATE 语句为当前读 , 加 X 锁;
  • 常见的 DML 语句(如 INSERT、DELETE、UPDATE)为当前读 , 加 X 锁;
  • 常见的 DDL 语句(如 ALTER、CREATE 等)加表级锁 , 且这些语句为隐式提交 , 不能回滚 。
其中 , 当前读的 SQL 语句的 where 从句的不同也会影响加锁 , 包括是否使用索引 , 索引是否是唯一索引等等 。
当前数据对加锁的影响SQL 语句执行时数据库中的数据也会对加锁产生影响 。
比如一条最简单的根据主键进行更新的 SQL 语句 , 如果主键存在 , 则只需要对其加记录锁 , 如果不存在 , 则需要在加间隙锁 。
至于其他非唯一性索引更新或者插入时的加锁也都不同程度的受到现存数据的影响 , 后续我们会一一说明 。
具体场景分析具体 SQL 场景分析主要借鉴何登成前辈的《MySQL 加锁处理分析》文章和 aneasystone 的系列文章 , 在他们的基础上进行了总结和整理 。
我们使用下面这张 book 表作为实例 , 其中 id 为主键 , ISBN(书号)为二级唯一索引 , Author(作者)为二级非唯一索引 , score(评分)无索引 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
UPDATE 语句加锁分析下面 , 我们先来分析 UPDATE 相关 SQL 在使用较为简单 where 从句情况下加锁情况 。其中的分析原则也适用于 UPDATE,DELETE 和 SELECT ... FOR UPDATE等当前读的语句 。


推荐阅读