MySQL官方文档14.5.1 InnoDB锁

原文地址:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html

14.5.1 InnoDB锁

本节介绍InnoDB使用的锁类型。

  • 共享和独占锁
  • 意向锁
  • 记录锁
  • 间隙锁
  • Next-Key 锁
  • 插入意向锁
  • AUTO-INC锁
  • 空间索引谓词锁

共享和独占锁

InnoDB使用两种类型的锁,共享(S)锁和独占(X)锁实现标准行级锁。

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

如果事务T1持有行r上的共享(S)锁,那么来自某个不同事务T2的对行r上的锁的请求按如下方式处理:

  • 可以立即授予T2对S锁的请求。T1和T2在r上都持有一个S锁。
  • T2对X锁的请求不能立即授予。

如果事务T1在行r上持有排他(X)锁,则不能立即授予来自某个不同事务T2的对r上任一类型的锁的请求。相反,事务T2必须等待事务T1释放其对行r的锁。

意向锁

InnoDB支持多粒度锁,允许行锁和表锁共存。 例如,诸如LOCK TABLES … WRITE之类的语句在指定的表上执行独占锁(X锁)。为了在多粒度级别实现锁,InnoDB使用意向锁。意向锁是表级锁,用于指示事务对表中某一行的事务需要哪种类型的锁(共享或排他)。有两种类型的意向锁:

  • 意向共享锁(IS)表示事务意图在表中的单个行上设置共享锁。
  • 意向排他锁(IX)表明事务意图在表中的单个行上设置独占锁。

例如,SELECT … LOCK IN SHARE MODE设置一个IS锁,而SELECT … FOR UPDATE设置一个IX锁。

意向锁协议如下:

  • 在事务可以获取表中某一行的共享锁之前,它必须首先获得一个IS锁或更高级别的锁。
  • 在一个事务可以获得一个表中某一行的排它锁之前,它必须首先在该表上获得一个IX锁。

表级锁类型的兼容性总结在下面的表格中。

锁类型XIXSIS
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创建一个隐藏的聚簇索引并使用这个索引进行记录锁定。请参见第14.8.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,无论该列中是否已有任何此类值。

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

对于使用唯一索引搜索唯一行的语句,不需要使用间隙锁。(这不包括搜索条件仅包含多列唯一索引中的某些列的情况。在这种情况下,会发生间隙锁定)。例如,如果id列具有唯一索引,则以下语句仅使用对于ID为100的行的索引记录锁,其他会话是否在上述间隔中插入行并不重要:

SELECT * FROM child WHERE id = 100;

如果id未被索引或者具有非唯一索引,则该语句将锁定前一个间隔。

这里也值得注意的是,不同事务可以在间隙上持有冲突的锁。例如,事务A可以在间隙上持有共享间隙锁(间隙S锁),而事务B在同一间隙上持有独占间隙锁(间隙X锁)。允许冲突间隙锁的原因是,如果从索引中清除记录,则必须合并由不同事务记录持有的间隙锁。

InnoDB中的间隙锁是“纯粹的抑制性”,这意味着它们只会阻止其他事务插入到间隙中。它们不会阻止不同的事务在同一个间隙上进行间隙锁定。因此,间隙X锁具有与间隙S锁相同的效果。

可以显式禁用间隙锁。如果将事务隔离级别更改为READ COMMITTED或启用innodb_locks_unsafe_for_binlog系统变量(现在已弃用),则会发生这种情况。在这些情况下,对搜索和索引扫描禁用间隙锁,仅用于外键约束检查和重复键检查。

使用READ COMMITTED隔离级别或启用innodb_locks_unsafe_for_binlog还有其他影响。在MySQL评估了WHERE条件后,释放不匹配行的记录锁。对于UPDATE语句,InnoDB执行“半连续”读取,以便将最新的提交版本返回给MySQL,以便MySQL可以确定该行是否与UPDATE的WHERE条件匹配。

Next-Key 锁

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

InnoDB以这样的方式执行行级锁定,即当它搜索或扫描表索引时,它会在遇到的索引记录上设置共享锁或排它锁,因此,行级锁实际上是索引记录锁。索引记录上的Next-Key锁还会影响该索引记录之前的“间隙”。 也就是说,Next-Key锁是索引记录锁,并在索引记录之前的间隔上加上间隙锁。如果一个会话对索引中的记录R具有共享或独占锁,则另一个会话不能在索引顺序中的R之前的间隙中插入新的索引记录。

假设索引包含值10、11、13和20。此索引的Next-Key锁涵盖以下区间,其中圆括号表示排除区间端点,方括号表示包含端点:

(负无穷, 10]
(10, 11]
(11, 13]
(13, 20]
(20, 正无穷)

对于最后一个间隔,Next-Key锁将索引中最大值以上的间隔锁定,并且“上确界”伪记录的值高于实际在索引中的任何值。“上确界“不是真实的索引记录,因此,实际上,这个Next-Key锁只锁定最大索引值之后的间隔。

默认情况下,InnoDB以REPEATABLE READ事务隔离级别运行。在这种情况下,InnoDB使用next-key锁进行搜索和索引扫描,从而防止幻读(请参见第14.5.4节“幻读”)。

next-key锁的事务数据在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配置选项控制用于auto-increment锁的算法。它允许您选择如何在可预测的自动递增值序列和插入操作的最大并发之间进行权衡。

有关更多信息,请参见第14.8.1.5节“InnoDB中的AUTO_INCREMENT处理”。

空间索引谓词锁

InnoDB支持包含空间列的列的空间索引(参见第11.5.8节“优化空间分析”)。

为了处理涉及空间索引的操作的锁定,Next-Key锁不能很好地支持REPEATABLE READ或SERIALIZABLE事务隔离级别。多维数据中没有绝对的排序概念,因此不清楚哪个是“next”key。

为了支持具有空间索引的表的隔离级别,InnoDB使用谓词锁。空间索引包含最小边界矩形(MBR)值,因此InnoDB通过对用于查询的MBR值设置谓词锁定来强制执行对索引的一致读取。其他事务不能插入或修改与查询条件匹配的行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值