### 15.7.1 InnoDB 锁定

[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)

本节描述了使用的锁类型`InnoDB`.

-   [共享锁和排它锁](innodb-locking.html#innodb-shared-exclusive-locks)

-   [意向锁](innodb-locking.html#innodb-intention-locks)

-   [记录锁](innodb-locking.html#innodb-record-locks)

-   [间隙锁](innodb-locking.html#innodb-gap-locks)

-   [下一键锁](innodb-locking.html#innodb-next-key-locks)

-   [插入意向锁](innodb-locking.html#innodb-insert-intention-locks)

-   [AUTO-INC 锁](innodb-locking.html#innodb-auto-inc-locks)

-   [空间索引的谓词锁](innodb-locking.html#innodb-predicate-locks)

#### 共享锁和排它锁

`InnoDB`实现标准的行级锁定，其中有两种类型的锁，[共享 (`小号`) 锁](glossary.html#glos_shared_lock)和[独家的 （`X`) 锁](glossary.html#glos_exclusive_lock).

-   一种[共享 (`小号`） 锁](glossary.html#glos_shared_lock)允许持有锁的事务读取一行。

-   一个[独家的 （`X`） 锁](glossary.html#glos_exclusive_lock)允许持有锁的事务更新或删除一行。

    如果交易`T1`持有一个共享 (`小号`) 锁定行`r`，然后来自某个不同事务的请求`T2`锁定行`r`处理如下：

-   一个请求`T2`为`小号`可以立即授予锁定。结果，两者`T1`和`T2`举行`小号`锁上`r`.

-   一个请求`T2`为`X`不能立即授予锁定。

    如果一笔交易`T1`拥有独占 (`X`) 锁定行`r`, 来自某个不同事务的请求`T2`对于任何一种类型的锁`r`不能立即授予。相反，交易`T2`必须等待交易`T1`释放其对行的锁定`r`.

#### 意向锁

`InnoDB`支持*多粒度锁定*它允许行锁和表锁共存。例如，这样的语句[`锁定表...写入`](lock-tables.html)需要一个排他锁（一个`X`锁定）在指定的表上。为了使多粒度级别的锁定变得实用，`InnoDB`用途[意向锁](glossary.html#glos_intention_lock).意向锁是表级锁，它指示事务稍后对表中的行需要哪种类型的锁（共享或独占）。有两种类型的意图锁：

-   一个[意向共享锁](glossary.html#glos_intention_shared_lock)(`是`) 表示交易打算设置一个*共享*锁定表中的各个行。

-   一个[意向排他锁](glossary.html#glos_intention_exclusive_lock)(`九`) 表示事务打算对表中的各个行设置排他锁。

    例如，[`选择...分享`](select.html)设置一个`是`锁，和[`选择 ... 更新`](select.html)设置一个`九`锁。

    意图锁定协议如下：

-   在事务可以获取表中某行的共享锁之前，它必须首先获取一个`是`锁定或更坚固的桌子。

-   在事务可以获取表中行的排他锁之前，它必须首先获取一个`九`锁在桌子上。

    下面的矩阵总结了表级锁类型的兼容性。

|  | `X` | `九` | `小号` | `是` |
| --- | --- | --- | ---- | --- |
| `X` | 冲突 | 冲突 | 冲突 | 冲突 |
| `九` | 冲突 | 兼容的 | 冲突 | 兼容的 |
| `小号` | 冲突 | 冲突 | 兼容的 | 兼容的 |
| `是` | 冲突 | 兼容的 | 兼容的 | 兼容的 |

如果锁与现有锁兼容，则将锁授予请求事务，但如果与现有锁冲突则不会。事务等待直到释放冲突的现有锁。如果锁请求与现有锁冲突并且无法授予，因为这会导致[僵局](glossary.html#glos_deadlock)，发生错误。

除了全表请求（例如，[`锁定表...写入`](lock-tables.html)）。意图锁的主要目的是表明有人正在锁定一行，或者要锁定表中的一行。

意向锁的事务数据在[`显示引擎 INNODB 状态`](show-engine.html)和[InnoDB 监视器](innodb-standard-monitor.html)输出：

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

#### 记录锁

记录锁是对索引记录的锁。例如，`从 t 中选择 c1，其中 c1 = 10 进行更新；`防止任何其他事务插入、更新或删除值为`t.c1`是`10`.

记录锁总是锁定索引记录，即使定义的表没有索引。对于此类情况，`InnoDB`创建一个隐藏的聚集索引并将此索引用于记录锁定。看[第 15.6.2.1 节，“聚集索引和二级索引”](innodb-index-types.html).

记录锁的事务数据在[`显示引擎 INNODB 状态`](show-engine.html)和[InnoDB 监视器](innodb-standard-monitor.html)输出：

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

#### 间隙锁

间隙锁是对索引记录之间间隙的锁定，或对第一条索引记录之前或最后一条索引记录之后的间隙的锁定。例如`从t中选择c1，其中c1介于10和20之间，用于更新；`防止其他事务插入的值为`15`进入纵队`t、 c1`，无论列中是否已存在任何此类值，因为该范围内所有现有值之间的间距都已锁定。

间隙可能跨越单个索引值、多个索引值，甚至可能为空。

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

对于使用唯一索引锁定行以搜索唯一行的语句，不需要间隙锁定。（这不包括搜索条件仅包括多列唯一索引的某些列的情况；在这种情况下，会发生间隙锁定。）例如，如果`身份证件`列具有唯一的索引，下面的语句仅对具有唯一索引的行使用索引记录锁`身份证件`值100，其他会话是否在前面的间隙中插入行并不重要：

```
SELECT * FROM child WHERE id = 100;
```

如果`身份证件`如果没有索引或索引不唯一，则语句会锁定前面的间隙。

这里还值得注意的是，不同的事务可能会在间隙上持有冲突的锁。例如，事务A可以在某个间隙上持有共享间隙锁（间隙S锁），而事务B在同一间隙上持有独占间隙锁（间隙X锁）。允许存在冲突的间隙锁的原因是，如果从索引中清除记录，则必须合并由不同事务保留在记录上的间隙锁。

间隙锁定`InnoDB`是“纯粹抑制性的”，这意味着它们的唯一目的是防止其他交易插入缺口。间隙锁可以共存。一个事务占用的间隙锁并不阻止另一个事务占用同一间隙的间隙锁。共享和独占间隙锁之间没有区别。它们彼此不冲突，并且执行相同的功能。

可以显式禁用间隙锁定。如果将事务隔离级别更改为[`阅读承诺`](innodb-transaction-isolation-levels.html#isolevel_read-committed)。在这种情况下，对搜索和索引扫描禁用间隙锁定，仅用于外键约束检查和重复键检查。

使用[`阅读承诺`](innodb-transaction-isolation-levels.html#isolevel_read-committed)隔离级别。不匹配行的记录锁在MySQL评估`哪里`条件对于`使现代化`声明，`InnoDB`执行“半一致”读取，以便将最新提交的版本返回给MySQL，以便MySQL可以确定该行是否与`哪里`情况[`使现代化`](update.html).

#### 下一把钥匙锁

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

`InnoDB`执行行级锁定的方式是，当它搜索或扫描表索引时，会对遇到的索引记录设置共享或排他锁。因此，行级锁实际上是索引记录锁。索引记录上的下一个键锁也会影响该索引记录之前的“间隔”。也就是说，下一个密钥锁是索引记录锁加上索引记录前面的间隙锁。如果一个会话有共享或独占锁定记录`R`在索引中，另一个会话不能在紧接之前的间隙中插入新的索引记录`R`按索引顺序排列。

假设索引包含值10、11、13和20。此索引可能的下一个键锁包括以下间隔，其中圆括号表示排除间隔端点，方括号表示包含端点：

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

对于最后一个时间间隔，下一个键锁会锁定索引中最大值和“上确界”伪记录上方的间隙，该伪记录的值高于索引中的任何实际值。上确界不是真正的索引记录，因此，实际上，下一个键锁只锁定最大索引值后面的间隙。

默认情况下，`InnoDB`经营于[`可重复读取`](innodb-transaction-isolation-levels.html#isolevel_repeatable-read)事务隔离级别。在这种情况下，`InnoDB`对搜索和索引扫描使用下一个键锁，这可以防止出现幻影行（请参阅[第15.7.4节，“幻影行”](innodb-next-key-locking.html)).

下一个钥匙锁的事务数据与中的以下内容类似：[`显示引擎INNODB状态`](show-engine.html)和[InnoDB监视器](innodb-standard-monitor.html)输出：

```
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.html)行插入之前的操作。此锁表示插入的意图，即如果插入到同一索引间隙中的多个事务没有插入到间隙中的同一位置，则它们不需要等待彼此。假设存在值为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开始一项事务，将一条记录插入gap。事务在等待获取排他锁时接受插入意图锁。

```
mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);
```

插入意图锁的事务数据与中的以下内容类似：[`显示引擎INNODB状态`](show-engine.html)和[InnoDB监视器](innodb-standard-monitor.html)输出：

```
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_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)变量控制用于自动增量锁定的算法。它允许您选择如何在可预测的自动增量值序列和插入操作的最大并发性之间进行权衡。

有关更多信息，请参阅[第15.6.1.6节“自动\_InnoDB中的增量处理”](innodb-auto-increment-handling.html).

#### 空间索引的谓词锁

[](<>)[](<>)

`InnoDB`支持`空间的`包含空间数据的列的索引（请参见[第11.4.9节，“优化空间分析”](optimizing-spatial-analysis.html)).

处理涉及以下操作的锁定：`空间的`索引，下一个键锁定无法很好地支持[`可重复读取`](innodb-transaction-isolation-levels.html#isolevel_repeatable-read)或[`可序列化`](innodb-transaction-isolation-levels.html#isolevel_serializable)事务隔离级别。多维数据中没有绝对的排序概念，因此不清楚哪个是“下一个”键。

为具有`空间的`索引，`InnoDB`使用谓词锁。A.`空间的`索引包含最小边框（MBR）值，所以`InnoDB`通过对用于查询的MBR值设置谓词锁，对索引强制执行一致读取。其他事务无法插入或修改与查询条件匹配的行。
