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


当 SQL 语句无法使用索引时 , 会进行全表扫描 , 这个时候 MySQL 会给整张表的所有数据行加记录锁 , 再由 MySQL Server 层进行过滤 。但是 , 在 MySQL Server 层进行过滤的时候 , 如果发现不满足 WHERE 条件 , 会释放对应记录的锁 。这样做 , 保证了最后只会持有满足条件记录上的锁 , 但是每条记录的加锁操作还是不能省略的 。
所以更新操作必须要根据索引进行操作 , 没有索引时 , 不仅会消耗大量的锁资源 , 增加数据库的开销 , 还会极大的降低了数据库的并发性能 。
间隙锁还是最开始更新用户年龄的例子 , 如果 id = 49 这条记录不存在 , 这个 SQL 语句还会加锁吗?答案是可能有 , 这取决于数据库的隔离级别 。这种情况下 , 在 RC 隔离级别不会加任何锁 , 在 RR 隔离级别会在 id = 49 前后两个索引之间加上间隙锁 。
间隙锁是一种加在两个索引之间的锁 , 或者加在第一个索引之前 , 或最后一个索引之后的间隙 。这个间隙可以跨一个索引记录 , 多个索引记录 , 甚至是空的 。使用间隙锁可以防止其他事务在这个范围内插入或修改记录 , 保证两次读取这个范围内的记录不会变 , 从而不会出现幻读现象 。
值得注意的是 , 间隙锁和间隙锁之间是互不冲突的 , 间隙锁唯一的作用就是为了防止其他事务的插入 , 所以加间隙 S 锁和加间隙 X 锁没有任何区别 。
Next-Key 锁【把MySQL中的各种锁及其原理都画出来】Next-key锁是记录锁和间隙锁的组合 , 它指的是加在某条记录以及这条记录前面间隙上的锁 。假设一个索引包含 15、18、20  , 30 , 49 , 50 这几个值 , 可能的 Next-key 锁如下:
(-∞, 15] , (15, 18] , (18, 20] , (20, 30] , (30, 49] , (49, 50] , (50, +∞)通常我们都用这种左开右闭区间来表示 Next-key 锁 , 其中 , 圆括号表示不包含该记录 , 方括号表示包含该记录 。前面四个都是 Next-key 锁 , 最后一个为间隙锁 。和间隙锁一样 , 在 RC 隔离级别下没有 Next-key 锁 , 只有 RR 隔离级别才有 。还是之前的例子 , 如果 id 不是主键 , 而是二级索引 , 且不是唯一索引 , 那么这个 SQL 在 RR 隔离级别下就会加如下的 Next-key 锁 (30, 49](49, 50)
此时如果插入一条 id = 31 的记录将会阻塞住 。之所以要把 id = 49 前后的间隙都锁住 , 仍然是为了解决幻读问题 , 因为 id 是非唯一索引 , 所以 id = 49 可能会有多条记录 , 为了防止再插入一条 id = 49 的记录 。
插入意向锁插入意向锁是一种特殊的间隙锁(简写成 II GAP)表示插入的意向 , 只有在 INSERT 的时候才会有这个锁 。注意 , 这个锁虽然也叫意向锁 , 但是和上面介绍的表级意向锁是两个完全不同的概念 , 不要搞混了 。
插入意向锁和插入意向锁之间互不冲突 , 所以可以在同一个间隙中有多个事务同时插入不同索引的记录 。譬如在上面的例子中 , id = 30 和 id = 49 之间如果有两个事务要同时分别插入 id = 32 和 id = 33 是没问题的 , 虽然两个事务都会在 id = 30 和 id = 50 之间加上插入意向锁 , 但是不会冲突 。
插入意向锁只会和间隙锁或 Next-key 锁冲突 , 正如上面所说 , 间隙锁唯一的作用就是防止其他事务插入记录造成幻读 , 正是由于在执行 INSERT 语句时需要加插入意向锁 , 而插入意向锁和间隙锁冲突 , 从而阻止了插入操作的执行 。
不同类型锁的兼容矩阵不同类型锁的兼容下如下图所示 。

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

文章插图
 
其中 , 第一行表示已有的锁 , 第一列表示要加的锁 。插入意向锁较为特殊 , 所以我们先对插入意向锁做个总结 , 如下: