文章目录
- 前言
- 一、锁的分类
- 二、锁的特性
- 1、共享锁和独占锁
- 2、意向锁
- 3、标准记录锁 Record Locks
- 4、间隙锁Gap Locks
- 5、临键锁 Next-Key Locks
- 6、插入意向锁 Insert Intention Locks
- 7、自增主键锁 AUTO-INC Locks
- 8、空间索引锁 Predicate Locks for Spatial Indexes
- 9、长锁和短锁
- 三、锁的兼容性
- 1、表级锁的兼容性
- 2、行级锁的兼容性
- 总结
前言
Mysql中除了我们常说的共享锁和排他锁,你还能说出哪些锁类型?今天和大家分享下Mysql在InnoDB引擎下锁的分类。
一、锁的分类
在InnoDB引擎下,锁按照表级锁和行级锁可以如下划分:
二、锁的特性
1、共享锁和独占锁
InnoDB 实现了标准的行级锁定,其中有两种类型的锁定: 共享(S)锁定和独占(X)锁定。
- 共享(S)锁允许持有该锁的事务读取一行。
- 独占(X)锁允许持有锁的事务更新或删除行。
2、意向锁
InnoDB 支持允许行锁和表锁共存的多粒度锁。
意向锁是表级别的锁
,它指示事务稍后需要对表中的行使用哪种类型的锁(共享或独占)。
意向锁有2种类型:
-
意向共享锁(IS)
:事务在获取表中某行的共享锁之前,必须首先获取表中的 IS 锁或更强的 IS 锁。 -
意向排他锁(IX)
:在事务获取表中行的独占锁之前,它必须首先获取表上的 IX 锁/
//当向一个表中的记录添加共享锁时,会先给该表添加一个意向共享锁IS,注意FOR SHARE时Mysql8.0以后的语法
SELECT ... FOR SHARE
//当向一个表中的记录添加排他锁时,会先给该表添加一个意向排他锁IX
SELECT ... FOR UPDATE
IS锁和IX锁是表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录
;也就是说,其实IS锁和IX锁是兼容的,IX锁和IX锁是兼容的,IS和IS也是兼容的。
3、标准记录锁 Record Locks
在InnoDB中,行级锁也称为记录锁,都是添加在索引记录上的锁
。
标准记录锁的含义是只锁记录不锁间隙
。
记录锁始终锁定索引记录,即时表定义时没有索引,对于这种情况,InnoDB 创建一个隐藏的聚集索引,并使用该索引进行记录锁定。
示例:
如果表中存在id=5的记录,执行下面的语句将只会锁定id=5的记录。
//S型标准记录锁
select * from user where id = 5 FOR SHARE;
//X型标准记录锁
select * from user where id = 5 FOR UPDATE;
4、间隙锁Gap Locks
间隙锁是指在索引记录之间的间隙上的锁
,或在第一个索引记录之前或最后一个索引记录之后的间隙上的锁。实际上还是加在索引记录上,不过通过类型区分。
例如:
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
阻止其他事务将15的值插入到列 t.c1中,不管该列中是否已经有这样的值,因为范围内所有现有值之间的间隔都被锁定了。
间隔可能跨越单个索引值、多个索引值,甚至为空。
间隙锁是性能和并发性之间权衡的一部分,只适用于可重复读(Repeatable Read)和串行化(Serializable)隔离级别,核心目的是为了解决幻读问题
。
InnoDB 中的间隙锁唯一目的是防止其他事务插入到间隙中
。
间隙锁可以共存
。一个事务采用的间隙锁不会阻止另一个事务在同一个间隙上采用间隙锁。共享间隙锁和独占间隙锁之间没有区别。它们彼此之间没有冲突,而且它们执行相同的功能。
事务A:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.02 sec)
//通过范围查询添加间隙锁
mysql> select * from user where id > 25 FOR UPDATE;
Empty set
事务B:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.01 sec)
//通过范围查询添加间隙锁,没有出现锁等待,说明间隙锁可以共存,也就是兼容
mysql> select * from user where id > 25 FOR UPDATE;
Empty set
优缺点分析:
优点:解决了事务并发的幻读问题
缺点:因为query执行过程中通过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。
间隙锁有一个致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成锁定的时候无法插入锁定键值范围内任何数据。在某些场景下这可能会对性能造成很大的危害。
示例:
如果表中不存在id=5的记录,且事务隔离级别为默认的RR,则会给id=5所在的间隙加锁。
START TRANSACTION;
select * from user where id = 5 FOR UPDATE;
5、临键锁 Next-Key Locks
临键锁(Next-Key Locks)是索引记录上的记录锁和索引记录前的间隙锁的组合
。
理解为Record Lock+索引前面的Gap Lock
。记住了,锁住的是索引前面的间隙。比如一个索引包含值,10,11,13和20。那么,间隙锁的范围如下
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
Next-Key Locks的范围是左开右闭
。默认右边的记录也会加锁。
临键锁(Next-Key Locks)适用的事务隔离级别和间隙锁(Gap Locks)保持一致:可重复读(Repeatable Read)和串行化(Serializable)
InnoDB 执行行级锁定的方式是,当它搜索或扫描表索引时,它对遇到的索引记录设置共享锁或排他锁。因此,行级锁实际上是索引记录锁。索引记录上的临键锁(Next-Key Locks)也会影响该索引记录之前的“间隔”。也就是说,临键锁(Next-Key Locks)是索引标准记录锁(Record Locks)加上位于索引记录之前的间隙上的间隙锁(Gap Locks)。
6、插入意向锁 Insert Intention Locks
官方名称:LOCK_INSERT_INTENTION
,也称为插入意向锁:事务在等待时也需要在内存中生成一个锁结构,表明有事务想在某个间隙中插入新记录,但是现在处于等待状态。如下图所示:
插入意向锁是一种由插入行之前的 INSERT 操作设置的间隙锁。这个锁表明插入的意图,如果插入到同一索引间隙中的多个事务没有插入到间隙中的同一位置,那么它们就不需要等待对方。假设有值为4和7的索引记录。分别尝试插入值5和6的独立事务,每个事务在获得插入行上的独占锁之前使用插入意向锁锁锁定4和7之间的间隙,但不会相互阻塞,因为行是非冲突的。
只有向间隙锁锁定的间隙中间插入数据时,才会添加插入意向锁。并且插入意向锁是一种短锁,当插入语句执行完成后会立即释放,而不是等到事务提交或回滚再释放
。
7、自增主键锁 AUTO-INC Locks
AUTO-INC 锁是一种特殊的表级锁
,由插入具有 AUTO_INCREMENT 列的表的事务获取。在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务必须等待向该表中执行自己的插入操作,以便由第一个事务插入的行接收连续的主键值。
这也是多个事务批量插入数据时,最后仍然可以保证表的自增主键是连续的
原因。
8、空间索引锁 Predicate Locks for Spatial Indexes
InnoDB 支持对包含空间数据的列进行空间索引(参见第11.4.9节“优化空间分析”)。为了处理涉及空间索引的操作的锁定,next-key 锁定不能很好地支持 REPEATABLE READ 或 SERIALIZABLE 事务隔离级别。在多维数据中没有绝对排序的概念,因此不清楚哪个是“下一个”键。为了支持对具有 SPATIAL 索引的表的隔离级别,InnoDB 使用谓词锁。空间索引包含最小外接矩形(MBR)值,所以 InnoDB 通过对用于查询的 MBR 值设置谓词锁来强制对索引进行一致的读取。其他事务无法插入或修改与查询条件匹配的行。
主要是针对空间索引加锁的优化。
9、长锁和短锁
如果锁获取之后直到事务提交后才释放,这种锁称为长锁
,比如间隙锁(Gap Locks)。
如果锁在操作完成之后就被释放,这种锁称为短锁
,比如插入意向锁 (Insert Intention Locks)。
三、锁的兼容性
1、表级锁的兼容性
说明:
- 读读兼容,读写不兼容,写写不兼容
- 意向锁之间相互兼容
2、行级锁的兼容性
说明:
- 标准记录锁(Record)和临键锁(Next-Key)之间符合读写锁的规律:读读兼容,读写和写写不兼容。
- 间隙锁(Gap)的目的是防止其他事务插入数据到索引间隙中,所以Gap锁会阻塞插入意向锁。
- 间隙锁(Gap)之间相互兼容
- 间隙锁(Gap 和 临键锁(Next-Key)相互兼容。
核心就是如果锁定了实际的索引记录,则需要遵循读写锁的规则的兼容性
。
总结
本文主要是对Mysql的InnoDB下的锁的相关类型做了介绍。
重点需要关注如下几点:
1、行级锁和表级锁的分类。
2、各种锁的作用及特性,以及适用的隔离级别。
3、各种锁相互间的兼容性。
锁的兼容性本质上还是读写锁的那一套,无非就是添加了间隙锁和插入意向锁,核心就是用来解决幻读问题。