#### 15.6.1.6 自动\_InnoDB 中的增量处理

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

`InnoDB`提供了一种可配置的锁定机制，可以显着提高向表中添加行的 SQL 语句的可伸缩性和性能`自动递增`列。要使用`自动递增`机制与`InnoDB`表，一个`自动递增`列必须定义为某个索引的第一列或唯一列，以便可以执行与索引的等效操作`选择最大（*`ai_col`*)`查找表以获得最大列值。该索引不需要是`首要的关键`要么`独特`，但为了避免在`自动递增`列，建议使用这些索引类型。

本节介绍`自动递增`锁定模式，不同的使用含义`自动递增`锁定模式设置，以及如何`InnoDB`初始化`自动递增`柜台。

-   [InnoDB 自动\_增量锁定模式](innodb-auto-increment-handling.html#innodb-auto-increment-lock-modes)

-   [InnoDB 自动\_INCREMENT 锁定模式的使用含义](innodb-auto-increment-handling.html#innodb-auto-increment-lock-mode-usage-implications)

-   [InnoDB 自动\_INCREMENT 计数器初始化](innodb-auto-increment-handling.html#innodb-auto-increment-initialization)

-   [笔记](innodb-auto-increment-handling.html#innodb-auto-increment-notes)

##### InnoDB 自动\_增量锁定模式

[](<>)

本节介绍`自动递增`用于生成自动增量值的锁定模式，以及每种锁定模式如何影响复制。自动增量锁定模式在启动时使用[`innodb_autoinc_lock_mode`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)多变的。

以下术语用于描述[`innodb_autoinc_lock_mode`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)设置：

-   “[`插入`](insert.html)-like”语句

    在表中生成新行的所有语句，包括[`插入`](insert.html),[`插入...选择`](insert-select.html),[`代替`](replace.html),[`替换...选择`](replace.html)， 和[`加载数据`](load-data.html).包括“简单插入”、“批量插入”和“混合模式”插入。

-   “简单的插入”

    可以预先确定要插入的行数的语句（最初处理语句时）。这包括单排和多排[`插入`](insert.html)和[`代替`](replace.html)没有嵌套子查询但没有嵌套子查询的语句[`插入 ... 在重复密钥更新时`](insert-on-duplicate.html).

-   “批量插入”

    预先不知道要插入的行数（以及所需的自动增量值的数量）的语句。这包括[`插入...选择`](insert-select.html),[`替换...选择`](replace.html)， 和[`加载数据`](load-data.html)陈述，但不简单`插入`.`InnoDB`为`自动递增`在处理每一行时，一次列一列。

-   “混合模式插入”

    这些是“简单的insert”语句，用于指定某些（但不是全部）新行的自动增量值。下面是一个例子，其中`c1`是一个`自动增量`表的列`t1`:

    ```
    INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');
    ```

    另一种类型的“混合模式插入”是[`插入关于重复密钥更新`](insert-on-duplicate.html)，在最坏的情况下，实际上是[`插入`](insert.html)然后是[`使现代化`](update.html)，其中`自动增量`列可能在更新阶段使用，也可能不使用。

    有三种可能的设置[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)变量对于“传统”、“连续”或“交错”锁定模式，设置分别为0、1或2。从MySQL 8.0开始，交错锁模式([`innodb_autoinc_lock_mode=2`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode))是默认设置。在MySQL 8.0之前，默认为连续锁定模式([`innodb_autoinc_lock_mode=1`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)).

    MySQL 8.0中Interleave lock mode的默认设置反映了默认复制类型从基于语句的复制到基于行的复制的变化。基于语句的复制需要连续自动增量锁定模式，以确保以可预测且可重复的顺序为给定的SQL语句序列分配自动增量值，而基于行的复制对SQL语句的执行顺序不敏感。

-   [](<>) `innodb_autoinc_lock_mode=0`（“传统”锁定模式）

    传统的锁定模式提供了与之前相同的行为[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)引入了变量。传统的锁模式选项用于向后兼容性、性能测试，以及解决“混合模式插入”的问题，因为语义上可能存在差异。

    在这种锁定模式下，所有“类似插入”的语句都会获得一个特殊的表级别`汽车公司`锁定插入到具有`自动增量`柱。该锁通常保持在语句的末尾（而不是事务的末尾），以确保按可预测和可重复的顺序为给定的序列分配自动增量值[`插入`](insert.html)语句，并确保任何给定语句指定的自动增量值是连续的。

    在基于语句的复制中，这意味着在副本服务器上复制SQL语句时，自动增量列使用的值与源服务器上相同。执行多个命令的结果[`插入`](insert.html)语句是确定性的，副本复制的数据与源上的数据相同。如果由多个[`插入`](insert.html)语句是交错的，这是两个并发语句的结果[`插入`](insert.html)语句是不确定的，无法使用基于语句的复制可靠地传播到副本服务器。

    为了说明这一点，请考虑使用此表的示例：

    ```
    CREATE TABLE t1 (
      c1 INT(11) NOT NULL AUTO_INCREMENT,
      c2 VARCHAR(10) DEFAULT NULL,
      PRIMARY KEY (c1)
    ) ENGINE=InnoDB;
    ```

    假设有两个事务正在运行，每个事务都将行插入一个带有`自动增量`柱一个事务是使用[`插入选择`](insert-select.html)语句插入1000行，另一个语句使用[`插入`](insert.html)插入一行的语句：

    ```
    Tx1: INSERT INTO t1 (c2) SELECT 1000 rows from another table ...
    Tx2: INSERT INTO t1 (c2) VALUES ('xxx');
    ```

    `InnoDB`无法提前告知从中检索了多少行[`选择`](select.html)在[`插入`](insert.html)语句，并在语句进行时一次分配一个自动增量值。使用表级锁，保持到语句末尾，只有一个[`插入`](insert.html)关于表的陈述`t1`可以一次执行，不同语句生成的自动递增数不会交错。Tx1生成的自动增量值[`插入选择`](insert-select.html)语句是连续的，并且[`插入`](insert.html)Tx2中的语句比Tx1中使用的所有语句都小或大，具体取决于先执行哪个语句。

    只要SQL语句在从二进制日志重放时（使用基于语句的复制时，或在恢复场景中）以相同的顺序执行，结果就与Tx1和Tx2首次运行时相同。因此，表级锁一直保持到语句结束[`插入`](insert.html)使用自动增量的语句可用于基于语句的复制。然而，当多个事务同时执行insert语句时，这些表级锁限制了并发性和可伸缩性。

    在前面的示例中，如果没有表级锁，则[`插入`](insert.html)在Tx2中，这取决于语句的执行时间。如果[`插入`](insert.html)当[`插入`](insert.html)在Tx1运行时（而不是在其启动之前或完成之后），由两个[`插入`](insert.html)语句是不确定的，在不同的运行中可能会有所不同。

    在[连续的](innodb-auto-increment-handling.html#innodb-auto-increment-lock-mode-consecutive)锁定模式，`InnoDB`可以避免使用表级别`汽车公司`锁定预先知道行数的“simple insert”语句，并且仍然为基于语句的复制保留确定性执行和安全性。

    如果在恢复或复制过程中未使用二进制日志重播SQL语句，则[交错的](innodb-auto-increment-handling.html#innodb-auto-increment-lock-mode-interleaved)锁定模式可用于消除所有表格级别的使用`汽车公司`锁定以实现更高的并发性和性能，代价是允许语句分配的自动递增数之间存在间隙，并可能使并发执行语句分配的数交错。

-   [](<>) `innodb_autoinc_lock_mode=1`（“连续”锁定模式）

    在这种模式下，“批量插入”使用特殊的`汽车公司`表级锁，并将其保持到语句结束。这适用于所有人[`插入选择`](insert-select.html), [`代替选择`](replace.html)和[`加载数据`](load-data.html)声明。只有一份声明`汽车公司`锁可以一次执行。如果大容量插入操作的源表与目标表不同，则`汽车公司`在对从源表中选择的第一行执行共享锁定后，将执行目标表上的锁定。如果批量插入操作的源和目标是同一个表，则`汽车公司`在对所有选定行执行共享锁定后执行锁定。

    “简单插入”（要插入的行数提前知道）避免表级插入`汽车公司`通过在互斥锁（轻量级锁）的控制下获得所需数量的自动增量值进行锁定，该互斥锁仅在分配过程中保持，*不*直到语句完成。没有桌位`汽车公司`除非`汽车公司`锁由另一个事务持有。如果另一笔交易持有`汽车公司`锁，“简单插入”等待`汽车公司`锁，就好像它是一个“批量插入”。

    此锁定模式可确保[`插入`](insert.html)如果语句中的行数事先未知（并且在语句进行过程中会分配自动递增编号），则所有自动递增值都由任何“[`插入`](insert.html)-like“语句是连续的，基于语句的复制操作是安全的。

    简单地说，这种锁定模式显著提高了可伸缩性，同时可以安全地用于基于语句的复制。此外，与“传统”锁定模式一样，任何给定语句分配的自动递增数都是*连续的*.有*无变化*在语义上，与任何使用自动递增的语句的“传统”模式相比，有一个重要的例外。

    例外情况是“混合模式插入”，其中用户为`自动增量`列，用于多行“简单插入”中的部分（但不是全部）行。对于此类插入，`InnoDB`分配的自动增量值多于要插入的行数。但是，自动分配的所有值都是连续生成的（因此高于）最近执行的前一条语句生成的自动增量值。“多余”的数字丢失了。

-   [](<>) `innodb_autoinc_lock_mode=2`（“交错”锁定模式）

    在此锁定模式下，否“[`插入`](insert.html)-“like”语句使用表级别`汽车公司`锁定，并且可以同时执行多个语句。这是最快、最具可扩展性的锁定模式，但事实并非如此*不安全*当从二进制日志重放SQL语句时，使用基于语句的复制或恢复场景。

    在这种锁定模式下，自动增量值保证是唯一的，并且在所有并发执行的进程中单调递增。”[`插入`](insert.html)-比如“声明”。但是，由于多个语句可以同时生成数字（也就是说，数字的分配是非常重要的）*交错的*跨语句），为任何给定语句插入的行生成的值可能不是连续的。

    如果只执行“简单插入”语句，其中要插入的行数提前已知，则为单个语句生成的行数中没有间隔，除了“混合模式插入”之外。然而，当执行“批量插入”时，任何给定语句分配的自动增量值可能会有间隙。

##### InnoDB AUTO\_增量锁模式的使用含义

-   在复制中使用自动增量

    如果使用基于语句的复制，请设置[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)设置为0或1，并在源及其副本上使用相同的值。如果使用，则无法确保副本上的自动增量值与源上的自动增量值相同[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)=2（“交错”）或源和副本不使用相同锁定模式的配置。

    如果使用基于行或混合格式的复制，所有自动增量锁定模式都是安全的，因为基于行的复制对SQL语句的执行顺序不敏感（对于任何不安全的基于语句的复制，混合格式都使用基于行的复制）。

-   “丢失”自动增量值和序列间隔

    在所有锁定模式（0、1和2）中，如果生成自动增量值的事务回滚，则这些自动增量值将“丢失”。为自动增量列生成值后，无论[`插入`](insert.html)-like“语句是否已完成，以及包含的事务是否回滚。这样丢失的值不会被重用。因此，存储在数据库中的值可能存在间隙`自动增量`表的列。

-   为`自动增量`柱

    在所有锁定模式（0、1和2）中，如果用户为`自动增量`列[`插入`](insert.html), `InnoDB`将行视为未指定值，并为其生成新值。

-   将负值指定给`自动增量`柱

    在所有锁定模式（0、1和2）中，如果将负值指定给`自动增量`柱

-   如果`自动增量`值大于指定整数类型的最大整数

    在所有锁定模式（0、1和2）中，如果值大于可存储在指定整数类型中的最大整数，则自动递增机制的行为未定义。

-   “批量插入”的自动增量值中的间隙

    具有[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)设置为0（“传统”）或1（“连续”），任何给定语句生成的自动增量值都是连续的，没有间隙，因为表级别`汽车公司`锁一直保持到语句结束，一次只能执行一条这样的语句。

    具有[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)设置为2（“交错”），则“批量插入”生成的自动增量值中可能会有间隙，但仅当同时执行[`插入`](insert.html)-比如“声明”。

    对于锁定模式1或2，连续语句之间可能会出现间隙，因为对于批量插入，每个语句所需的自动增量值的确切数量可能未知，并且可能会高估。

-   “混合模式插入”指定的自动增量值

    考虑一个“混合模式插入”，其中一个“简单插入”指定一些（但不是全部）结果行的自动增量值。这种语句在锁定模式0、1和2下的行为不同。例如，假设`c1`是一个`自动增量`表的列`t1`，并且最近自动生成的序列号为100。

    ```
    mysql> CREATE TABLE t1 (
        -> c1 INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
        -> c2 CHAR(1)
        -> ) ENGINE = INNODB;
    ```

    现在，考虑下面的“混合模式插入”语句：

    ```
    mysql> INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');
    ```

    具有[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)设置为0（“传统”），四个新行是：

    ```
    mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    |   1 | a    |
    | 101 | b    |
    |   5 | c    |
    | 102 | d    |
    +-----+------+
    ```

    下一个可用的自动增量值是103，因为自动增量值一次分配一个，而不是在语句执行开始时一次分配全部。无论是否存在“并发执行”，该结果都是正确的[`插入`](insert.html)-比如“任何类型的陈述”。

    具有[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)设置为1（“连续”），四个新行还包括：

    ```
    mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    |   1 | a    |
    | 101 | b    |
    |   5 | c    |
    | 102 | d    |
    +-----+------+
    ```

    然而，在这种情况下，下一个可用的自动增量值是105，而不是103，因为在处理语句时分配了四个自动增量值，但只使用了两个。无论是否存在“并发执行”，该结果都是正确的[`插入`](insert.html)-比如“任何类型的陈述”。

    具有[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)设置为2（“交错”），四个新行是：

    ```
    mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    |   1 | a    |
    |   x | b    |
    |   5 | c    |
    |   y | d    |
    +-----+------+
    ```

    价值观*`十、`*和*`y`*是唯一的，并且比以前生成的任何行都大。但是*`十、`*和*`y`*取决于并发执行语句生成的自动增量值的数量。

    最后，考虑当最近生成的序列号为100时发出的以下语句：

    ```
    mysql> INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (101,'c'), (NULL,'d');
    ```

    任何[`innodb_autoinc_锁定模式`](innodb-parameters.html#sysvar_innodb_autoinc_lock_mode)设置时，此语句生成一个重复的密钥错误23000(`不会写字；表中的重复键`)因为101是为行分配的`（空，'b'）`以及行的插入`（101，‘c’）`失败。

-   修改`自动增量`序列中值的列值[`插入`](insert.html)声明

    在MySQL 5.7及更早版本中，修改`自动增量`序列中值的列值[`插入`](insert.html)语句可能会导致“重复输入”错误。例如，如果您执行了[`使现代化`](update.html)改变了一个世界的行动`自动增量`列值设置为大于当前最大自动增量值的值，后续[`插入`](insert.html)未指定未使用的自动增量值的操作可能会遇到“重复输入”错误。在MySQL 8.0及更高版本中，如果修改`自动增量`将列值设置为大于当前最大自动增量值的值，新值将被保留，然后[`插入`](insert.html)操作从新的较大值开始分配自动增量值。下面的示例演示了这种行为。

    ```
    mysql> CREATE TABLE t1 (
        -> c1 INT NOT NULL AUTO_INCREMENT,
        -> PRIMARY KEY (c1)
        ->  ) ENGINE = InnoDB;

    mysql> INSERT INTO t1 VALUES(0), (0), (3);

    mysql> SELECT c1 FROM t1;
    +----+
    | c1 |
    +----+
    |  1 |
    |  2 |
    |  3 |
    +----+

    mysql> UPDATE t1 SET c1 = 4 WHERE c1 = 1;

    mysql> SELECT c1 FROM t1;
    +----+
    | c1 |
    +----+
    |  2 |
    |  3 |
    |  4 |
    +----+

    mysql> INSERT INTO t1 VALUES(0);

    mysql> SELECT c1 FROM t1;
    +----+
    | c1 |
    +----+
    |  2 |
    |  3 |
    |  4 |
    |  5 |
    +----+
    ```

##### InnoDB AUTO\_增量计数器初始化

[](<>)

本节介绍如何`InnoDB`初始化`自动增量`柜台。

如果指定`自动增量`专栏`InnoDB`表中，内存中的表对象包含一个称为自动递增计数器的特殊计数器，在为列指定新值时使用该计数器。

在MySQL 5.7及更早版本中，自动递增计数器存储在主内存中，而不是磁盘上。要在服务器重新启动后初始化自动递增计数器，`InnoDB`将在第一次插入到包含`自动增量`柱

```
SELECT MAX(ai_col) FROM table_name FOR UPDATE;
```

在MySQL 8.0中，这种行为发生了改变。当前最大自动增量计数器值在每次更改时写入重做日志，并保存到每个检查点的数据字典中。这些更改使当前最大自动增量计数器值在服务器重启期间保持不变。

在正常关机后重新启动服务器时，`InnoDB`使用数据字典中存储的当前最大自动增量值初始化内存中的自动增量计数器。

在崩溃恢复期间重新启动服务器时，`InnoDB`使用数据字典中存储的当前最大自动增量值初始化内存中的自动增量计数器，并扫描重做日志以查找自上次检查点以来写入的自动增量计数器值。如果重做记录值大于内存计数器值，则应用重做记录值。然而，在服务器意外退出的情况下，无法保证重用先前分配的自动增值。每次当前最大自动增量值由于[`插入`](insert.html)或[`使现代化`](update.html)操作时，新值会写入重做日志，但如果在重做日志刷新到磁盘之前发生意外退出，则在服务器重新启动后初始化自动增量计数器时，可以重用以前分配的值。

只有在这种情况下`InnoDB`使用相当于`从中选择最大值（ai_col）*`表2\\u名称`*更新`初始化自动递增计数器的语句是[导入表](innodb-table-import.html)没有`.cfg`元数据文件。否则，当前最大自动增量计数器值将从`.cfg`元数据文件（如果存在）。除了计数器值初始化，等效于`从中选择最大值（ai_col）*`表2\\u名称`*`语句用于在尝试使用`改变桌子。。。自动增量=*`N`*更新`陈述例如，删除一些记录后，可以尝试将计数器值设置为较小的值。在这种情况下，必须搜索该表，以确保新计数器值不小于或等于当前实际最大计数器值。

在MySQL 5.7及更早版本中，服务器重启会取消`自动增量=N`表选项，可用于`创建表格`或`改变桌子`语句分别设置初始计数器值或更改现有计数器值。在MySQL 8.0中，服务器重启不会取消`自动增量=N`表格选项。如果将自动递增计数器初始化为特定值，或者将自动递增计数器值更改为更大的值，则新值将在服务器重新启动时保留。

笔记

[`改变桌子。。。自动增量=N`](alter-table.html)只能将自动递增计数器值更改为大于当前最大值的值。

在MySQL 5.7及更早版本中，服务器在发生故障后立即重启[`回降`](commit.html)操作可能会导致重用以前分配给回滚事务的自动增量值，从而有效地回滚当前最大自动增量值。在MySQL 8.0中，当前的最大自动增量值被保留，防止重用以前分配的值。

如果[`显示表格状态`](show-table-status.html)语句在自动递增计数器初始化之前检查表，`InnoDB`打开表，并使用存储在数据字典中的当前最大自动增量值初始化计数器值。然后，该值存储在内存中，供以后的插入或更新使用。计数器值的初始化使用表上的正常独占锁定读取，该锁定持续到事务结束。`InnoDB`为用户指定的自动增量值大于0的新创建表初始化自动增量计数器时，遵循相同的过程。

自动递增计数器初始化后，如果插入行时未明确指定自动递增值，`InnoDB`隐式递增计数器并将新值赋给列。如果插入明确指定自动递增列值的行，且该值大于当前最大计数器值，则计数器将设置为指定值。

`InnoDB`只要服务器运行，就使用内存中的自动增量计数器。当服务器停止并重新启动时，`InnoDB`如前所述，重新初始化自动递增计数器。

这个[`自动增量偏移`](replication-options-source.html#sysvar_auto_increment_offset)变量决定了`自动增量`列值。默认设置为1。

这个[`自动增量`](replication-options-source.html#sysvar_auto_increment_increment)变量控制连续列值之间的间隔。默认设置为1。

##### 笔记

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

当`自动增量`整数列的值已用完，后续`插入`操作返回重复的密钥错误。这是MySQL的一般行为。
