## 15.3 InnoDB 多版本

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

`InnoDB`是一个多版本的存储引擎。它保留有关已更改行的旧版本的信息，以支持并发和回滚等事务功能。此信息存储在称为回滚段的数据结构中的撤消表空间中。看[第 15.6.3.4 节，“撤消表空间”](innodb-undo-tablespaces.html).`InnoDB`使用回滚段中的信息来执行事务回滚中所需的撤消操作。它还使用这些信息来构建行的早期版本以进行一致的读取。看[第 15.7.2.3 节，“一致的非锁定读取”](innodb-consistent-read.html).

在内部，`InnoDB`为存储在数据库中的每一行添加三个字段：

-   一个 6 字节`DB_TRX_ID`字段指示插入或更新行的最后一个事务的事务标识符。此外，删除在内部被视为更新，其中行中的特殊位设置为将其标记为已删除。

-   一个 7 字节`DB_ROLL_PTR`字段称为滚动指针。滚动指针指向写入回滚段的撤消日志记录。如果行已更新，则撤消日志记录包含在更新之前重建行内容所需的信息。

-   一个 6 字节`DB_ROW_ID`字段包含一个行 ID，该行 ID 随着新行的插入而单调增加。如果`InnoDB`自动生成聚集索引，索引包含行 ID 值。否则，该`数据库行ID`列不会出现在任何索引中。

    回滚段中的撤消日志分为插入和更新撤消日志。插入撤消日志仅在事务回滚时需要，事务提交后即可丢弃。Update undo日志也用于一致性读取，但只有在不存在用于一致性读取的事务时，才能丢弃它们`InnoDB`已分配一个快照，在一致读取中，该快照可能需要更新撤消日志中的信息来生成数据库行的早期版本。有关撤消日志的更多信息，请参阅[第15.6.6节“撤销日志”](innodb-undo-logs.html).

    建议定期提交事务，包括只发出一致读取的事务。否则`InnoDB`无法丢弃更新撤消日志中的数据，回滚段可能会变得太大，填满它所在的撤消表空间。有关管理撤消表空间的信息，请参阅[第15.6.3.4节，“撤消表空间”](innodb-undo-tablespaces.html).

    回滚段中撤消日志记录的物理大小通常小于相应的插入或更新行。您可以使用这些信息来计算回滚段所需的空间。

    在`InnoDB`在多版本控制方案中，当您使用SQL语句删除某一行时，该行不会立即从数据库中物理删除。`InnoDB`只有在放弃为删除而编写的更新撤消日志记录时，才会物理删除相应的行及其索引记录。这种删除操作称为清除，速度非常快，通常与执行删除的SQL语句的时间顺序相同。

    如果以大约相同的速率在表中以小批量插入和删除行，则清除线程可能会开始落后，并且由于所有“死”行，表可能会变得越来越大，使所有内容都绑定在磁盘上，速度非常慢。在这种情况下，可以限制新行操作，并通过调整[`innodb_max_purge_lag`](innodb-parameters.html#sysvar_innodb_max_purge_lag)系统变量。有关更多信息，请参阅[第15.8.9节“净化配置”](innodb-purge-configuration.html).

### 多版本控制和二级索引

`InnoDB`多版本并发控制（MVCC）以不同于聚集索引的方式处理二级索引。聚集索引中的记录会就地更新，其隐藏的系统列指向撤消日志项，从中可以重建早期版本的记录。与聚集索引记录不同，二级索引记录不包含隐藏的系统列，也不会就地更新。

更新二级索引列时，旧的二级索引记录将被删除标记，新记录将被插入，删除标记的记录最终将被清除。当二级索引记录被删除标记或二级索引页被更新的事务更新时，`InnoDB`在聚集索引中查找数据库记录。在聚集索引中，记录的`DB_TRX_ID`如果在读取事务启动后修改了记录，则会选中，并从撤消日志中检索记录的正确版本。

如果二级索引记录被标记为删除，或者二级索引页面被更新的事务更新，则[覆盖指数](glossary.html#glos_covering_index)技术没有被使用。而不是从索引结构返回值，`InnoDB`在聚集索引中查找记录。

然而，如果[指数条件下推（ICP）](index-condition-pushdown-optimization.html)已启用优化，并且`哪里`条件只能使用索引中的字段进行评估，MySQL服务器仍然会推送这部分内容`哪里`条件下降到存储引擎，在那里使用索引对其进行评估。如果没有找到匹配的记录，则避免进行聚集索引查找。如果找到匹配的记录，即使在删除标记的记录中，`InnoDB`在聚集索引中查找记录。
