摘要
本文基于MySQL5.7官方文档介绍InnoDB所有的锁类型。
到底有多少种锁?
以下总结自MySQL 5.7 官方文档,其中第6-8种锁还是第一次听说。
-
共享锁和排它锁——Shared and Exclusive Locks
-
意向锁——Intention Locks
-
记录锁——Record Locks
-
间隙锁——Gap Locks
-
Next-Key Locks
-
Insert Intention Locks
-
AUTO-INC Locks
-
Predicate Locks for Spatial Indexes(用于空间索引的谓词锁)
共享锁和排它锁——Shared and Exclusive Locks
InnoDB最大的特性在于“行锁”。在并发访问数据的情况下,这将会带来非常大的性能提升。因为表中的数据可以根据要读的行来分别进行加锁,InnoDB不需要锁住整个表。这两种类型的行锁又称为:shared (S) locks and exclusive (X) locks。下面是对这两种锁的介绍,以及互斥规则。
- 共享锁:允许一个事务锁住它要“读”的行。
- 事务T1:持有行 r 的(S)锁。
- 事务T2:
- 请求行 r 的(S)锁将立即成功。
- 请求行 r 的 (X)锁将被阻塞。
- 排它锁:允许一个事物锁住它要“
Update
或delete
”的行。-
事务T1:持有行 r 的(X)锁。
-
事务T2:无论持有哪种锁都会被阻塞,直到T1释放排它锁。
-
意向锁——Intention Locks
InnoDB支持“多粒度加锁”方式,简单来说,就是可以支持对表加锁和对行加锁。
含义:意向锁属于“表锁”级别。它表示一个事务“已经”对表中某一行加了锁,或者说“将要”对某一行加锁。意向锁不会阻塞其他事务,除非把整个表都锁住的语句:LOCK TABLES ... WRITE
。
-
意向共享锁(IS):表明一个事务倾向于对表中某一行加(S)锁。
-
意向排它锁(IX):表明一个事务倾向于对表中某行加(X)锁。
例如,SELECT ... LOCK IN SHARE MODE
设置IS锁,而SELECT ... FOR UPDATE
设置IX锁。
意向锁加锁原则如下:
-
对某行加S锁之前必须先获得IS锁或者IX锁。
-
对某行加X锁之前必须先获得IX锁。
锁的兼容性如下:无论是IX或IS,都与其他IX或IS兼容。
X | IX | S | IS | |
---|---|---|---|---|
X | 冲突 | 冲突 | 冲突 | 冲突 |
IX | 冲突 | 兼容 | 冲突 | 兼容 |
S | 冲突 | 冲突 | 兼容 | 兼容 |
IS | 冲突 | 兼容 | 兼容 | 兼容 |
一个事务能否获取到锁,取决于这个锁是否与表中已经存在的锁兼容。如果兼容,那么将申请成功;否则,事务只能等待其他人释放掉锁。
记录锁——Record Locks
含义:对单个索引记录加锁,就是InnoDB真正使用的“行锁”。它只会锁住索引记录,如果表里面没有索引,那么InnoDB会隐式的创建一个聚集索引。
例如,SELECT * FROM child WHERE id = 100;
将只会使用记录锁。
Tips:这里额外补充一点,InnoDB会从上到下的顺序创建聚集索引:
- 主键id列。
- non-null且unique约束的列。
- 前两种类型的列都没有,那么InnoDB将隐式创建一个聚集索引列。
间隙锁——Gap Locks
含义:只锁住Records之间的间隙,此外也可以锁住第一条记录之前或者最后一条记录之后的间隙。但不会锁定记录本身。
例如:SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
将会锁住10-20之间的值,这样可以防止其他事务尝试插入一个值为15
的数据。
有两点细节需要注意:
- Gap Lock只存在于
REPEATABLE READ
,READ COMMITTED
并发时不会使用。 - 同一个Gap可以被多个事务持有S或X锁。例如事务A持有了一个Gap的(gap S-lock),事务B也可以对这个Gap持有(gap X-lock)。
Next-Key Locks
含义:Record Lock + Gap Lock的组合。即锁住所有满足条件的记录和其中第一条记录之前的“一个”及之后的“所有”间隙。
例如,表中有数据10, 11, 13, 20
。next-key lock可以锁住的区间有:
(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, +∞)
InnoDB通过这个锁来解决“幻读”问题(MySQL官方将我之前理解的“不可重复读”定义为幻读,所以其实它们俩是同一东西)。
实例1:假如表中有数据1, 2, 5
,事务A执行SELECT * FROM t WHERE a > 2 FOR UPDATE
。next-key lock会对(2, +∞)
这个区间加(X)锁,因此在这区间内的Insert
都是不允许的,从而解决了幻读问题。
Insert Intention Locks
含义:一种当执行“插入一行”操作时设置的gap lock。
AUTO-INC Locks
含义:一种当事务插入AUTO_INCREMENT
列时会设置的“表级别”锁。