这个部分我们介绍一下InnoDB所使用的锁。
[TOC]
InnoDB 实现了标准的行级锁,主要分为两类:共享锁和排他锁。
如果事务T1获得某记录(r)的一个共享锁(s),那么就r记录来说,来自其他事务(T2)的请求会按照下面两种情况被处理:
如果T1事务获取了对r记录的x排他锁,那么来自其他事务(T2)的请求,无论是s锁请求还是x锁请求,都无法立即获取,而是等待T1释放了对r记录的x锁后才能获取。
Innodb 支持多粒度加锁,这种锁允许事务同时对记录和表加锁。为了实现多粒度加锁,我们创建了一种新类型的锁即意向锁。在Innodb中,意向锁本身属于表级锁,它将决定事务在请求对某行记录加锁时使用共享锁还是排他锁。相对应的,意向锁也分为两种:
比如,select … lock in share mode 将会尝试获得IS锁,select … for update 则尝试获得IX锁。
意向锁的分配协议如下:
简单的总结下,这两个规则可以被描述为以下的锁类型匹配矩阵
type | X | IX | s | IS |
---|---|---|---|---|
x | 互斥 | 互斥 | 互斥 | 互斥 |
IX | 互斥 | 共存 | 互斥 | 共存 |
S | 互斥 | 互斥 | 共存 | 共存 |
IS | 互斥 | 共存 | 共存 | 共存 |
两锁互斥代表事务在已经获取了其中某个锁后无法再获取另外一个锁,共存同理。在互斥的情况下,一个事务如果已经获得了某个锁,当它尝试获得另外一个锁时会进入等待队列。当一个锁请求与当前获得的锁冲突并且无法获得锁(为了防止死锁)时,会报错。
所以,意向锁不会锁任何东西,除非事务直接尝试对整个表加锁(比如:lock tables … write)。IX和IS 锁主要是用来表达某个事务正在使用某一记录,或者将要锁住表中的某行。
意向锁的事务结构在 SHOW ENGINE INNODB STATUS 和InnoDB Monitor 中表现为类似于下面这样:
TABLE LOCK table `test`.`t` trx id 10080 lock mode IX
间隙锁是对指定记录之间间隙所加的锁,这个间隙包括:记录与记录之间的间隙,第一条记录之前的间隙以及最后一条记录后面的间隙。举个例子:select c1 from t where c1 between 10 and 20 for update 将防止其他事务插入15这个数字到t.c1字段中,无论是否已经有一个这样的值在这个字段中了,因为值在这个范围内的所有记录都被锁住了。
NK锁是记录锁和目标记录前部间隙锁的结合体。当InnoDB数据库搜索扫描表索引时,会对匹配了索引条件的索引记录进行加锁(s锁或者x锁),这就是行级锁的实现方式。所以行级锁实际上就是指索引记录锁。NK锁不仅会锁住索引记录,同时也会影响这条索引记录之前的间隙。这就是说,一个NK锁=索引记录锁 + 记录的前间隙锁。如果一个事务会话已经拥有了对索引中记录R的共享或者排他锁,另外一个事务将无法在此索引记录之前插入一个新的索引记录。
假设一个索引序列包含了10,11,13,20。在这个索引中,可能的NK锁包含以下锁:
(负无穷, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive 正无穷)
其中,圆括号代表不包括当前边界,方括号则代表包含当前边界值。
在最后一个间隔中,NK锁将会锁住从20到最大值之间的区间,这个最大值伪记录比当前索引里的任何一个值都大。这个伪记录不是一个真实的索引记录。所以,事实上,NK锁只是锁住了最大索引记录的尾部。
默认来说,InnoDB的事务隔离级别为REPEATABLE READ。在这种情况下,InnoDB使用NK锁来搜索和扫描索引,可以防止幻读(phantom read)。
NK锁的事务结构在 SHOW ENGINE INNODB STATUS 和InnoDB Monitor 中表现为类似于下面这样:
RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10080 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 8000000a; asc ;;
1: len 6; hex 00000000274f; asc 'O;;
2: len 7; hex b60000019d0110; asc ;;