关于后端:MySQL-InnoDB-的所有锁

2次阅读

共计 4572 个字符,预计需要花费 12 分钟才能阅读完成。

InnoDB 锁介绍

共享锁和独占锁

InnoDB 实现规范的行级锁定,其中有两种类型的锁,共享 (S) 锁和独占 (X) 锁。

  • 共享 (S) 锁容许持有锁的事务读取一行。
  • 独占 (X) 锁容许持有锁的事务更新或删除行。

如果事务在 row 上 T1 持有共享 (S) 锁 r,则来自某个不同事务 T2 的对 row 锁的申请 r 将按如下形式解决:

  • 能够立刻授予锁 T2 申请。S 后果,T1 和 T2 都 S 锁定了 r.
  • 不能立刻授予锁定 T2 申请。

如果事务在 row 上 T1 持有排他 (X) 锁,则无奈立刻授予 r 来自某个不同事务 T2 对任一类型锁的申请。r 相同,事务 T2 必须期待事务 T1 开释其对 row 的锁定 r。

意向锁

InnoDB 反对多粒度锁,容许行锁和表锁并存。例如,诸如 在指定表上 LOCK TABLES … WRITE 获取排他锁(一个锁)的语句。X 为了使多粒度级别的锁定变得切实可行,InnoDB 请应用 意向锁。意向锁是表级锁,批示事务稍后对表中的行须要哪种类型的锁(共享或独占)。意向锁有两种类型:

  • 动向共享锁 (IS) 示意事务打算在表中的各个行上设置 共享 锁。
  • 动向排他锁 ()IX 示意事务打算在表中的各个行上设置排他锁。

例如,SELECT … FOR SHARE 设置一个 IS 锁,并 SELECT … FOR UPDATE 设置一个 IX 锁。

动向锁定协定如下:

  • 在事务能够获取表中行的共享锁之前,它必须首先获取 IS 表上的锁或更强锁。
  • 在事务能够获取表中一行的排他锁之前,它必须首先获取 IX 表上的锁。

下表总结了表级锁类型兼容性。

X IX S IS
X 抵触 抵触 抵触 抵触
IX 抵触 兼容的 抵触 兼容的
S 抵触 抵触 兼容的 兼容的
IS 抵触 兼容的 兼容的 兼容的

如果申请事务与现有锁兼容,则将锁授予申请事务,但如果它与现有锁抵触,则不会授予该锁。事务期待,直到开释抵触的现有锁。如果锁定申请与现有锁定抵触并且因为会导致死锁而无奈授予,则会产生谬误。

意向锁不会阻塞除全表申请(例如,LOCK TABLES … WRITE)之外的任何内容。意向锁的次要目标是表明有人正在锁定一行,或者将要锁定表中的一行。

意向锁的事务数据在 InnoDB 监视器 输入 SHOW ENGINE INNODB STATUS 中 相似于以下内容:

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

记录锁

记录锁是索引记录上的锁。例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 避免任何其余事务插入、更新或删除值为 的 t.c1 行 10。

记录锁总是锁定索引记录,即便表没有定义索引。对于这种状况,InnoDB 创立一个暗藏的聚簇索引并应用该索引进行记录锁定。请参阅 第 15.6.2.1 节,“聚簇索引和二级索引”。

记录锁的事务数据在 InnoDB 监视器 输入 SHOW ENGINE INNODB STATUS 中 相似于以下内容:

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        ;;

间隙锁

间隙锁是在索引记录之间的间隙上的锁,或者是在第一条索引记录之前或最初一条索引记录之后的间隙上的锁。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; 阻止其余事务将值插入 15 到列 t.c1 中,无论该列中是否曾经存在任何此类值,因为范畴内所有现有值之间的间隙都已锁定。

间隙可能逾越单个索引值、多个索引值,甚至是空的。

间隙锁是性能和并发性之间衡量的一部分,并且用于某些事务隔离级别而不是其余事务隔离级别。

对于应用惟一索引锁定行以搜寻惟一行的语句,不须要间隙锁定。(这不包含搜寻条件只包含多列惟一索引的某些列的状况;在这种状况下,的确会产生间隙锁定。)例如,如果该 id 列具备惟一索引,则以下语句仅应用一个索引记录锁定 id 值为 100 的行,其余会话是否在后面的间隙中插入行并不重要:

SELECT * FROM child WHERE id = 100;

如果 id 未索引或具备非惟一索引,则该语句会锁定后面的间隙。

这里还值得注意的是,不同事务能够在间隙上持有抵触锁。例如,事务 A 能够在一个间隙上持有一个共享间隙锁(间隙 S 锁),而事务 B 在同一间隙上持有一个独占间隙锁(间隙 X 锁)。容许抵触间隙锁的起因是,如果从索引中革除记录,则必须合并不同事务在记录上持有的间隙锁。

间隙锁 InnoDB 是“纯正抑制性的”,这意味着它们的惟一目标是避免其余事务插入间隙。间隙锁能够共存。一个事务获取的间隙锁不会阻止另一个事务在同一间隙上获取间隙锁。共享和排他间隙锁之间没有区别。它们彼此不抵触,并且它们执行雷同的性能。

能够明确禁用间隙锁定。如果您将事务隔离级别更改为,则会产生这种状况 READ COMMITTED。在这种状况下,间隙锁定在搜寻和索引扫描中被禁用,并且仅用于外键束缚检查和反复键查看。

READ COMMITTED 应用隔离级别 还有其余影响。WHERE 在 MySQL 评估条件后,开释不匹配行的记录锁。对于 UPDATE 语句,InnoDB 进行“半统一”读取,这样它将最新提交的版本返回给 MySQL,以便 MySQL 能够确定该行是否匹配 WHERE .UPDATE

Next-Key Locks

下一个键锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。

InnoDB 执行行级锁定的形式是,当它搜寻或扫描表索引时,它会在遇到的索引记录上设置共享或排他锁。因而,行级锁实际上是索引记录锁。索引记录上的下一个键锁也会影响该索引记录之前的“间隙”。也就是说,下一个键锁是索引记录锁加上索引记录之前的间隙上的间隙锁。如果一个会话在索引中的记录上具备共享锁或独占锁 R,则另一个会话无奈 R 在索引程序之前的空隙中插入新的索引记录。

假如一个索引蕴含值 10、11、13 和 20。该索引可能的 next-key 锁涵盖以下区间,其中圆括号示意排除区间端点,方括号示意蕴含端点:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

对于最初一个工夫距离,next-key 锁锁定索引中最大值上方的间隙以及 值高于索引中理论值的“supremum”伪记录。supremum 不是真正的索引记录,因而,实际上,这个下一个键锁只锁定最大索引值之后的间隙。

默认状况下,InnoDB 在 REPEATABLE READ 事务隔离级别运行。在这种状况下,InnoDB 应用下一个键锁进行搜寻和索引扫描,以防止出现幻象行。

next-key 锁的事务数据在 InnoDB 监视器 输入 SHOW ENGINE INNODB STATUS 中 相似于以下内容:

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 在行插入之前由操作设置的间隙锁。这个锁示意插入的用意,这样插入到同一个索引间隙中的多个事务如果没有插入到间隙中的雷同地位,则不须要互相期待。假如有值为 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 监视器 输入 SHOW ENGINE INNODB STATUS 中 相似于以下内容:

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 锁

锁是一种 AUTO-INC 非凡的表级锁,由插入到具备 AUTO_INCREMENT 列的表中的事务获取。在最简略的状况下,如果一个事务正在向表中插入值,则任何其余事务必须期待本人向该表中插入,以便第一个事务插入的行接管间断的主键值。

该 innodb_autoinc_lock_mode 变量管制用于主动增量锁定的算法。它容许您抉择如何在可预测的主动增量值序列和插入操作的最大并发性之间进行衡量。

空间索引的谓词锁

为了解决波及 SPATIAL 索引的操作的锁定,下一个键锁定不能很好地反对 REPEATABLE READ 或 SERIALIZABLE 事务隔离级别。多维数据没有相对排序的概念,所以不分明哪个是“next”键。

SPATIAL 要启用对带索引 的表的隔离级别的反对,请应用谓词锁。索引蕴含 SPATIAL 最小边界矩形 (MBR) 值,因而 InnoDB 通过在用于查问的 MBR 值上设置谓词锁来强制对索引进行统一读取。其余事务无奈插入或批改与查问条件匹配的行。

本文由 mdnice 多平台公布

正文完
 0