InnoDB锁类型
InnoDB引擎使用了八种类型的锁,他们分别是:
- 共享排他锁(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 实现了标准的行级锁并且他有两种锁的类型,共享锁(S)和排它锁(X):
- 共享锁允许持有锁的事物读取一行数据
- 排它锁允许持有锁的事物删除或者更新一行数据
如果事物T1持有r行共享锁(S),那么事物T2希望对r行上锁的处理如下:
- T2请求一个S锁,会立即准许。结果,T1和T2两个都会持有S锁。
- T2请求一个X锁,不会立即准许。(猜测可能会等待)
如果事物T1对r行持有排它锁(X),另一个事物T2的请求则不能够被立即准许持有r行任何一种类型的锁。相反,事物T2不得不等待事物T1释放r行锁。
意向锁(Intention Locks)
InnoDB支持多粒度锁,允许表锁和行锁共存。例如,就像一个表级锁/写( LOCK TABLES … WRITE)也会带有一个排它锁(X)[释义:排它锁属于行级
]。为了实现多力度级别锁,InnoDB使用了意向锁(Intention Locks)。意向锁是表级别的锁,它表明事务稍后对表中的行需要哪种类型的锁(共享或独占)。意向锁有两种类型:
- 意向共享锁(IS)表明一个事物打算在表的单个行设置共享锁。
- 意向排它锁(IX)表明一个事物打算在表的单个行设置排它锁。
例如,执行SELECT ... LOCK IN SHARE MODE
的时候设置一个IS锁,和执行 SELECT ... FOR UPDATE
的时候设置IX锁。
这个意向锁的协议如下:
一个事物能够在表中获取行级共享锁之前,它必须首先在一个表中获得一个IS锁或者更强类型的锁。
一个事物能够在表中获取行级排它锁之前,它必须首先在一个表中获得一个IX锁。
表级别锁类型的兼容性总结如下表所示:
排它锁 | 意向排它锁 | 共享锁 | 意向共享锁 | |
---|---|---|---|---|
排它锁 | x | x | x | x |
意向排它锁 | x | y | x | y |
共享锁 | x | x | y | y |
意向共享锁 | x | y | y | y |
如果一个事物请求锁(A)与已存在的锁(B)兼容,则该事物可以得到锁(A);但是,如果不兼容则该事物不会得到锁(A)。事物(A)会一直等待直到锁(B)被释放为止。如果锁(A)请求与已存在锁(B)冲突,则该请求将不能够得到锁(A),因为它可能引发死锁或错误。
除了全表请求以外意向锁不会被阻塞(例如:LOCK TABLES ... WRITE
),意向锁最主要的目的是显示某人锁住一行数据或者打算锁住表中的一行数据。
在InnoDB引擎显示和监控输出中意向锁的事物数据如下所示:
TABLE LOCK table `test`.`t` trx id 10080 lock mode IX
记录锁(Record Locks)
记录锁是索引记录上的锁。例如:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE
;预防任何事物从t.c1 = 10.
的行插入,更新,和删除额操作。
记录锁总是锁定索引记录,即使表中没有定义索引。对于这种情况,InnoDB 创建了隐式的聚簇索引并使用它来锁定记录。参考:聚簇索引和二级索引
在InnoDB引擎的显示和监控输出中记录锁的事物数据如下所示:
RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
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 ;;
间隙锁(Gap Locks)
间隙锁是指在索引范围之间加锁,或者是在第一条索引记录之前或者是最后一条索引记录之后的间隙进行加锁。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
防止其他事物能够从t.c1
列插入值为15的数据,不论列中是否已经存在这样的记录值,因为该范围内所有已经存在的值都已被锁定。
间隙可能跨越单个索引,多个索引,甚至是空索引。
隙锁是权衡了性能和并发能力的一个折中选择,在某些隔离级别中使用了间隙锁。
使用唯一索引搜索唯一行时,不需要使用间隙锁。(这并不包括在查找条件中,条件列只是多列唯一索引中的一部分时;此时,间隙锁定会发生)。例如,如果id列有一个惟一的索引,下面的语句只对id值为100的行使用索引记录锁,其他会话是否在前面的空格中插入行并不重要:
SELECT * FROM child WHERE id = 100;
如果id不是索引,或者不是一个唯一索引,则这条语句就会锁住之前的间隙。
在这里值得注意的是,不同的事物可以在间隙上持有冲突锁。例如:事物A在间隙上持有一个共享间隙锁 (gap S-lock) ,同时事物B在相同的间隙上也持有一个排它锁(gap X-lock) 。允许间隙锁冲突的原因是,如果从索引中清除一条记录,则在不同事物中持有的间隙锁记录必须合并。
间隙锁在InnoDB中“完全被抑制的”,这意味着它们的唯一目的是阻止其它事物插入此间隙。间隙锁可以共存。一个事物所采取的间隙锁并不能够阻止其他事物采取相同间隙的间隙锁。共享间隙锁和排它间隙锁是相同。它们之间是不冲突的,并且执行相同的功能。
如果改变事物隔离级别为READ COMMITTED
或者启用系统变量innodb_locks_unsafe_for_binlog
就可以明确的禁用间隙锁。在这种情况下,对搜索和索引扫描禁用间隙锁,只用于外键约束检查和重复键检查。
使用 READ COMMITTED
隔离级别或启用 innodb_locks_unsafe_for_binlog
还有其他影响。 MySQL评估 WHERE
条件后,将释放非匹配行的记录锁。对于UPDATE
语句,InnoDB 执行“半一致(semi-consistent)
”读取,以便将最新提交的版本返回给MySQL,以便MySQL可以确定该行是否与 UPDATE
的WHERE
条件匹配。
临键锁 (Next-Key Locks)
next-key锁是索引记录上的记录锁和索引记录前间隙上的间隙锁的组合。
InnoDB执行行级锁的方式是,当它搜索或扫描一个表索引时,它会在遇到的索引记录上设置共享锁或排他锁。因此,行级锁实际上是索引记录锁。索引记录上的next-key锁还会影响该索引记录之前的“间隙”。也就是说,next-key锁是索引记录锁加上索引记录之前的间隙锁。如果一个会话对索引中的记录R有共享或排他锁,则另一个会话不能在索引顺序中紧靠R之前的间隙插入新的索引记录。
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
对于最后一个时间间隔,next-key锁锁定索引中最大值和“上极”伪记录(其值比实际索引中的任何值都高)之上的差额。上限值并不是一个真正的索引记录,因此,这个next-key锁实际上只锁定了最大索引值之后的间隙。
默认情况下,InnoDB操作在可重复读事务隔离级别。在这种情况下,InnoDB使用next-key锁进行搜索和索引扫描,这可以防止幻像行(见14.7.4节,“幻像行”)。
下一个键锁定的事务数据类似于如下显示引擎INNODB状态和INNODB监视器输出:
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 ;;
插入意图锁 (Insert Intention Locks)
插入意图锁是一种间隙锁,由行插入之前的插入操作设置。如果多个插入到同一个索引间隙的事务没有在间隙内的相同位置插入,则该锁将以这样的方式表示插入的意图:多个插入到同一个索引间隙的事务不需要相互等待。假设有值为4和7的索引记录。分别尝试插入值为5和6的独立事务,在获得插入行的排他锁之前,都使用插入意图锁锁住4和7之间的间隙,但不会相互阻塞,因为这两行不冲突。
下面的示例演示了一个事务,该事务在获得插入记录上的排他锁之前使用插入意图锁。该示例涉及两个客户机,A和B。
客户机A创建一个包含两个索引记录(90和102)的表,然后启动一个事务,该事务对ID大于100的索引记录施加排他锁。排他锁包括记录102前的间隙锁:
mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);
mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id |
+-----+
| 102 |
+-----+
客户端B开始一个事务,向间隙插入一条记录。事务在等待获得排他锁时,采用插入意图锁。
mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);
插入意图锁的事务数据类似于如下显示引擎INNODB状态和INNODB监视器输出:
RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000066; asc f;;
1: len 6; hex 000000002215; asc " ;;
2: len 7; hex 9000000172011c; asc r ;;...
自增锁 (AUTO-INC Locks)
AUTO-INC锁是一种特殊的表级锁,通过事务插入到具有AUTO_INCREMENT列的表中来实现。在最简单的情况下,如果一个事务将值插入到表中,那么任何其他事务都必须等待对该表进行自己的插入,以便由第一个事务插入的行接收到连续的主键值。
innodb_autoinc_lock_mode配置选项控制用于自动增量锁定的算法。它允许您选择如何在自动递增值的可预测序列和插入操作的最大并发性之间进行权衡。
空间锁和预测锁(Predicate Locks for Spatial Indexes)
InnoDB支持对包含空间列的列进行空间索引。
为了处理涉及空间索引的操作的锁,next-key锁在支持可重复读取或可序列化事务隔离级别时不能很好地工作。在多维数据中没有绝对的排序概念,因此不清楚哪一个是“下一个”键。
为了支持具有空间索引的表的隔离级别,InnoDB使用了谓词锁。空间索引包含最小边界矩形(MBR)值,因此InnoDB通过对用于查询的MBR值设置谓词锁来强制对索引进行一致读取。其他事务不能插入或修改与查询条件匹配的行。
声明
原文地址:
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
本文只做学习参考,如有任何不准确的地方欢迎指正。(翻译的不好请见谅!)