目 录CONTENT

文章目录

【MySQL】深入了解InnoDB Locking

EulerBlind
2025-07-01 / 0 评论 / 0 点赞 / 1 阅读 / 0 字

*共享锁和独占锁*

InnoDB实现标准行级锁定 其中有两种类型的锁,共享锁(S)和排他锁(X)。

  • 共享锁(S)允许事务 持有锁以读取一行。
  • 排他锁(X)允许事务 持有更新或删除行的锁。

如果事务对某行持有共享()锁,那么来自某个不同事务对该行的锁的请求将按以下方式处理:T1SrT2r [Tn 表示事务编号,S表示锁类型,r表示请求]

  • 对于一个锁的请求,可以立即被授予。因此,T1和T2都对该行持有一个锁。T2ST1T2Sr
  • 对于一个锁的请求,无法立即被授予。T2X

如果一个事务对某行持有独占()锁,那么来自某个不同事务对该行的任一类型的锁的请求都无法立即被授予。相反,事务必须等待事务释放对该行的锁。T1XrT2rT2T1r

意向锁

InnoDB支持多粒度锁定,允许行锁和表锁共存。例如,诸如LOCK TABLES ... WRITE这样的语句会在指定的表上获取独占锁(一个锁)。为了使多粒度级别的锁定变得实用,InnoDB使用了意向锁。意向锁是表级别的锁,指示事务稍后对表中的某一行需要哪种类型的锁(共享或独占)。意向锁有两种类型:XInnoDB

  • 意向共享锁(IS)表示事务打算在表中的个别行上设置共享锁。
  • 意向独占锁(IX)表示事务打算在表中的个别行上设置独占锁。

例如,SELECT ... FOR SHARE设置一个IS锁,而SELECT ... FOR UPDATE设置一个IX锁。

意向锁定协议如下:

  • 在事务可以在表中的某一行上获取共享锁之前,它必须首先在表上获取一个IS锁或更强的锁。
  • 在事务可以在表中的某一行上获取独占锁之前,它必须首先在表上获取一个IX锁。

表级别锁类型的兼容性总结在以下矩阵中:

X IX S IS
X Conflict Conflict Conflict Conflict
IX Conflict Compatible Conflict Compatible
S Conflict Conflict Compatible Compatible
IS Conflict Compatible Compatible Compatible

如果一个请求的锁与现有锁兼容,那么该锁将被授予给请求的事务,但如果它与现有锁冲突,就不会被授予。事务将等待冲突的现有锁被释放。如果锁请求与现有锁冲突,并且由于可能导致死锁而无法授予锁,就会发生错误。

意向锁不会阻塞任何东西,除非是全表请求(例如,LOCK TABLES ... WRITE)。意向锁的主要目的是显示有人正在锁定表中的某一行,或者将要锁定表中的某一行。

SHOW ENGINE INNODB STATUSInnoDB监视器输出中,意向锁的事务数据看起来类似于以下内容:

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

点击并拖拽以移动

记录锁

记录锁是对索引记录的锁定。例如,****SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 阻止任何其他事务在**c1**的值为10的行上插入、更新或删除。

记录锁始终锁定索引记录,即使表定义中没有索引。对于这种情况,InnoDB会创建一个隐藏的聚簇索引,并在记录锁定时使用该索引。请参阅第15.6.2.1节,“Clustered and Secondary Indexes”

SHOW ENGINE INNODB STATUSInnoDB监视器输出中,记录锁的事务数据看起来类似于以下内容:

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; 防止其他事务在列 c1 中插入值为15的值,无论该列中是否已经存在任何此类值,因为该范围内所有现有值之间的间隙都被锁定。

一个间隙可以跨越单个索引值,多个索引值,甚至可以是空的。

间隙锁是性能和并发性之间的权衡的一部分,并且在某些事务隔离级别中使用,而在其他级别中则不使用。

**对于使用唯一索引搜索唯一行的语句,不需要间隙锁。(这不包括搜索条件只包含多列唯一索引的某些列的情况;在这种情况下,确实会发生间隙锁。)例如,如果列 **id 有一个唯一索引,以下语句仅使用索引记录锁定具有值100的行,而不关心其他会在前面的间隙中插入行的会话:

SELECT * FROM child WHERE id = 100;

点击并拖拽以移动

如果列 **id 没有建立索引或者具有非唯一索引,该语句确实会锁定前面的间隙。id**

这里还值得注意的是,不同事务可以在同一个间隙上持有相互冲突的锁。例如,事务A可以在一个间隙上持有共享间隙锁(gap S-lock),而事务B在同一个间隙上持有独占间隙锁(gap X-lock)。允许冲突的间隙锁的原因是,如果从索引中删除了一条记录,不同事务在该记录上持有的间隙锁必须被合并。

InnoDB中的间隙锁是“纯粹的抑制性”的,这意味着它们唯一的目的是防止其他事务向间隙插入数据。间隙锁可以共存。由一个事务获取的间隙锁不会阻止另一个事务在同一个间隙上获取间隙锁。共享和独占间隙锁之间没有区别。它们不会相互冲突,执行相同的功能。

可以显式禁用间隙锁。如果将事务隔离级别更改为READ COMMITTED,则禁用对搜索和索引扫描的间隙锁,并仅用于外键约束检查和重复键检查。

使用READ COMMITTED隔离级别还有其他影响。对于不匹配的行,非匹配的记录锁将在MySQL评估条件后释放。对于UPDATE语句,进行“半一致性”读取,使其返回MySQL的最新提交版本,以便MySQL可以确定行是否符合UPDATE的条件。

Next-Key锁

Next-Key锁是对索引记录的记录锁和对索引记录之前的间隙的间隙锁的组合。

InnoDB以一种使得在搜索或扫描表索引时,它对遇到的索引记录设置共享或独占锁的方式执行行级锁定。因此,行级锁实际上是索引记录锁。对索引记录的Next-Key锁也会影响该索引记录之前的“间隙”。换句话说,Next-Key锁是对索引记录的索引记录锁和对该索引记录之前间隙的间隙锁的组合。如果一个会话对索引中的某个记录拥有共享或独占锁,另一个会话就不能在该记录之前的索引顺序中立即插入新的索引记录。RR

假设一个索引包含值10、11、13和20。对于这个索引,可能的Next-Key锁覆盖以下间隔,其中圆括号表示排除间隔端点,方括号表示包含端点:

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

点击并拖拽以移动

对于最后一个间隔,Next-Key锁锁定了索引中最大值上方的间隙以及具有高于实际索引中任何值的值的“supremum”伪记录。Supremum不是一个真实的索引记录,因此实际上,这个Next-Key锁只锁定了最大索引值之后的间隙。

默认情况下,InnoDB在REPEATABLE READ事务隔离级别中运行。在这种情况下,使用Next-Key锁进行搜索和索引扫描,以防止幻象行(参见第15.7.4节,“幻象行”)。

SHOW ENGINE INNODB STATUSInnoDB监视器输出中,Next-Key锁的事务数据看起来类似于以下内容:

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 STATUSInnoDB监视器输出中,插入意向锁的事务数据看起来类似于以下内容:

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

点击并拖拽以移动

自增锁

自增锁是由插入到带有自增列的表中的事务获取的特殊表级别的锁。在最简单的情况下,如果一个事务正在向表中插入值,任何其他事务必须等待执行它们自己的插入操作,以便由第一个事务插入的行接收连续的主键值。

innodb_autoinc_lock_mode变量控制用于自增锁定的算法。它允许您选择在可预测的自增值序列和插入操作的最大并发性之间进行权衡。

有关更多信息,请参阅第15.6.1.6节,“InnoDB中的AUTO_INCREMENT处理”

空间索引的谓词锁

InnoDB支持对包含空间数据的列进行索引(参见第11.4.9节,“优化空间分析”)。SPATIAL

为了处理涉及索引的操作的锁定,Next-Key锁定对于支持REPEATABLE READ或SERIALIZABLE事务隔离级别的情况效果不佳。在多维数据中,没有绝对的排序概念,因此不清楚哪个是“下一个”键。SPATIAL

为了启用对带有索引的表的隔离级别的支持,InnoDB使用谓词锁。一个索引包含最小包围矩形(MBR)值,因此通过在用于查询的MBR值上设置谓词锁来强制在索引上进行一致的读取。其他事务无法插入或修改与查询条件匹配的行。SPATIALInnoDBSPATIALInnoDB

0

评论区