文章摘要(AI生成)
原址: https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-predicate-locks共享锁和独占锁InnoDB实现了标准的行级锁定,其中有两种类型的锁: 共享 ( S) 锁和排它 ( X) 锁。共享( S) 锁允
原址: https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-predicate-locks
共享锁和独占锁
InnoDB
实现了标准的行级锁定,其中有两种类型的锁: 共享 ( S
) 锁和排它 ( X
) 锁。
如果事务T1
在行r
上持有共享 (S
) 锁,则来自某个不同事务的 T2
对行r
的获取锁请求将按如下方式处理:
- 事务
T2
的请求能够立刻获取到S
锁,进而持有锁进行读操作 - 事务
T2
的请求不能获取到X
锁。
如果事务T1
持有行r
的独占 (X
) 锁,则无法立即授予事务T2
对行r
任一类型的锁的请求。相反,事务T2
必须等待事务T1
释放其对行r
的锁定。
意向锁
InnoDB
支持多粒度锁定,允许行锁和表锁共存。例如,诸如LOCK TABLES ... WRITE
此类的语句在指定的表上采用排他锁(X
锁)。为了使多粒度级别的锁定切实可行,InnoDB
使用 意图锁实现。意向锁是表级锁,表示事务稍后需要对表中的行使用哪种类型的锁(共享或独占)。意向锁有两种类型:
比如:SELECT ... FOR SHARE
语句用来设置IS
锁, SELECT ... FOR UPDATE
语句用来设置IX
锁。
意向锁定协议如下:
- 在事务可以获取表中行的共享锁之前,它必须首先获取表上的
IS
锁或更强的锁。 - 在事务可以获取表中行的排他锁之前,它必须首先获取表上的
IX
锁。
表级锁类型兼容性总结在以下矩阵中。
X |
IX |
S |
IS |
|
---|---|---|---|---|
X |
冲突 | 冲突 | 冲突 | 冲突 |
IX |
冲突 | 兼容的 | 冲突 | 兼容的 |
S |
冲突 | 冲突 | 兼容的 | 兼容的 |
IS |
冲突 | 兼容的 | 兼容的 | 兼容的 |
如果请求事务与现有锁兼容,则将锁授予该事务,但如果与现有锁冲突,则不会授予该锁。事务会等待,直到释放冲突的现有锁。如果锁请求与现有锁冲突并且由于会导致死锁而无法被授予 ,则会发生错误。
意向锁不会阻止除全表请求之外的任何内容(例如,LOCK TABLES ... WRITE
)。意向锁的主要目的是表明有人正在锁定一行,或者将要锁定表中的一行。
意向锁的事务数据在使用SHOW ENGINE INNODB STATUS
语句查看InnoDB 监视器时,会输出以下类似内容 :
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 节“聚集索引和二级索引”。
记录锁的事务数据在使用SHOW ENGINE INNODB STATUS
语句查看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 ;;
间隙锁
间隙锁是对索引记录之间间隙的锁定,或者对第一个索引记录之前或最后一个索引记录之后的间隙的锁定。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
可以防止其他事务将t.c1
的值插入15
到20
的列中 ,无论该列中是否已经存在任何此类值,因为该范围内的所有现有值之间的间隙已被锁定。
间隙可能跨越单个索引值、多个索引值,甚至是空的。
间隙锁是性能和并发性之间权衡的一部分,并且在某些事务隔离级别中使用,而在其他事务隔离级别中则不使用。
对于使用唯一索引锁定行来搜索唯一行的语句,不需要间隙锁定。(这不包括搜索条件仅包括多列唯一索引的某些列的情况;在这种情况下,确实会发生间隙锁定。)例如,如果id
列有唯一索引,则以下语句仅使用索引记录锁定id
值为100 的行,并且其他会话是否在前面的间隙中插入行并不重要:
SELECT * FROM child WHERE id = 100;
如果id
未建立索引或具有非唯一索引,则该语句会锁定前面的间隙。
这里还值得注意的是,不同事务可以在间隙上持有冲突锁。例如,事务 A 可以在某个间隙上持有共享间隙锁(间隙 S 锁),而事务 B 在同一间隙上持有独占间隙锁(间隙 X 锁)。允许冲突间隙锁的原因是,如果从索引中清除记录,则必须合并不同事务在该记录上持有的间隙锁。
间隙锁InnoDB
是“纯粹抑制性的”,这意味着它们的唯一目的是防止其他事务插入到间隙中。间隙锁可以共存。一个事务获取的间隙锁不会阻止另一事务在同一间隙上获取间隙锁。共享间隙锁和独占间隙锁之间没有区别。它们彼此不冲突,并且执行相同的功能。
如果将事务隔离级别更改为 READ COMMITTED
,间隙锁定会被显式禁用。在这种情况下,间隙锁定对搜索和索引扫描禁用,仅用于外键约束检查和重复键检查。
使用 READ COMMITTED
隔离级别还有其他影响:MySQL 评估WHERE
条件后,将释放不匹配行的记录锁。对于 UPDATE
语句,InnoDB
是否进行“半一致”读取,从而将最新提交的版本返回给 MySQL,以便 MySQL 可以确定该行是否WHERE
符合UPDATE
.
临键锁
临键锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。
InnoDB
执行行级锁定的方式是,当它搜索或扫描表索引时,它会在遇到的索引记录上设置共享锁或独占锁。因此,行级锁实际上是索引记录锁。索引记录上的下一键锁定也会影响该索引记录之前的“间隙”。也就是说,临键锁是索引记录锁加上索引记录之前间隙上的间隙锁。如果一个会话对索引中的 R
记录具有共享锁或独占锁 ,则另一个会话无法R
在索引顺序中紧邻之前的间隙中插入新索引记录。
假设索引包含值 10、11、13 和 20。该索引可能的下一键锁涵盖以下区间,其中圆括号表示排除区间端点,方括号表示包含端点:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
对于最后一个间隔,临键锁锁定索引中最大值之上的间隙以及具有高于索引中实际值的值的“ supremum ”伪记录。上界不是真正的索引记录,因此,实际上,此临键锁仅锁定最大索引值之后的间隙。
默认情况下,在事务隔离级别InnoDB
下运行 REPEATABLE READ
。在这种情况下,InnoDB
使用临键锁进行搜索和索引扫描,这可以防止幻像行(请参阅第 15.7.4 节 “幻像行”)。
临键锁的事务数据在使用SHOW ENGINE INNODB STATUS
语句查看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
操作预设置。此锁表明插入的意图是,插入同一索引间隙的多个事务如果没有插入间隙内的同一位置,则无需互相等待。假设存在值为 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);
插入意向锁的事务数据在使用SHOW ENGINE INNODB STATUS
语句查看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 锁
AUTO-INC
锁是插入到包含AUTO_INCREMENT
列的表中的事务所采用的特殊表级锁 。在最简单的情况下,如果一个事务正在将值插入表中,则任何其他事务都必须等待才能向该表中执行自己的插入操作,以便第一个事务插入的行接收连续的主键值。
变量innodb_autoinc_lock_mode
用于控制自动增量锁定的算法。它允许您选择如何在可预测的自动增量值序列和插入操作的最大并发度之间进行权衡。
有关更多信息,请参见 第 15.6.1.6 节,“InnoDB 中的 AUTO_INCRMENT 处理”。
空间索引的谓词锁
InnoDB
支持SPATIAL
包含空间数据的列的索引(请参见 第 11.4.9 节,“优化空间分析”)。
为了处理涉及 SPATIAL
索引的操作的锁定,下一键锁定不能很好地支持REPEATABLE READ
或 SERIALIZABLE
事务隔离级别。多维数据中不存在绝对的排序概念,因此并不清楚哪个是 “下一个”键。
要支持带 SPATIAL
索引的表的隔离级别,InnoDB
请使用谓词锁。索引SPATIAL
包含最小边界矩形 (MBR) 值,因此 InnoDB
通过在用于查询的 MBR 值上设置谓词锁来强制对索引进行一致读取。其他事务无法插入或修改与查询条件匹配的行。
评论区