[翻译] MySQL 之 InnoDB 中的 Multi-Version 特性

mysql

官方文档:15.3 InnoDB Multi-Versioning

一、多版本存储引擎 --- InnoDB

InnoDB 是一个多版本存储引擎 multi-versioned storage engine,它保存了 修改行的新、老两个版本 的数据信息,以支持事务的并发和回滚。

1. 回滚片段 --- rollback segment

InnoDB 在表空间中,用一个叫 回滚片段 (rollback segment) 的数据结构,保存了行的新、老两个版本 的数据信息。实现了:

  • InnoDB 可以使用这个 回滚片段 (rollback segment)执行事务的回滚
  • InnoDB 可以使用这个 回滚片段 (rollback segment) 为一致性读 consistent read(也称为当前度 current read),构建行数据的早期版本。
2. multi-version 的具体实现方式

在内部,InnoDB 为每行数据增加了三个字段:

  • DB_TRX_ID,6-byte:
    • DB_TRX_ID 存储了:插入或修改这条数据的 最后一个 事务的 ID。
    • 由于在 InnoDB 内部,删除被看做是一种 特殊的更新DB_TRX_ID 中有一个 特殊位 标识了是否是 删除 状态。
  • DB_ROLL_PTR,7-byte:
    • DB_ROLL_PTR 是一个回退指针 roll pointer,指向了:由 rollback 语句 写入 undo log 中的一条记录 record
    • 当行数据被更新时,这条 undo log 中的记录 record,包含了:将行数据恢复到 行数据更新前的数据 的全部信息。
  • DB_ROW_ID,6-byte:
    • DB_ROW_ID 保存了:每行数据插入时生成的 自增ID
    • 如果这个 InnoDB 的表中包含了一个聚簇索引 clustered index(包含了一个主键列),DB_ROW_ID 中存储的 行自增ID,也会保存到这个聚簇索引 clustered index 中。
    • 如果这个 InnoDB 的表中没有包含了一个聚簇索引 clustered index(没有主键列),DB_ROW_ID 中存储的 行自增ID 不会出现在任何索引中。
3. 回滚日志 undo log 的生命周期及一些建议
  • 回滚日志 undo log 分为:
    • 为了插入数据的回滚日志 insert undo log
    • 为了更新数据的回滚日志 update undo log
  • 插入数据的回滚日志,为了支持 事务回滚功能(当要删除数据的事务回滚时),且当事务提交时会马上删除。
  • 更新数据的回滚日志,为了支持一致性读 consistent read(也称为当前度 current read),且当 没有任何事务在使用「更新回滚日志」来构建行数据的早期版本,以支持「一致性读 consistent read(也称为当前度 current read)」 时会马上删除。
  • 定时提交你的事务,即使是那些只包含了一些一致性读 consistent read(也称为当前度 current read)的事务。否则,海量的 更新数据的回滚日志 无法删除,将沾满你的表空间。
  • 回滚日志 undo log 的实际物理大小一般会小于插入或更新的行,因此你可以大概计算出,事务所产生的用户事务回滚的回滚日志的大小。
  • 在多版本的 InnoDB 表中,当你使用 delete语句 删除某些行时,这些行并不会马上进行物理删除,而是要等到 InnoDB 删除了那条 delete语句 所对应的回滚日志时,才会对 行数据和对应的索引数据 进行物理删除。这个过程叫做 purge,它和执行 delete语句 时一样快。
  • 如果你按照一定的速度,一批一批的删除或新增数据,上述的 purge 工作会 持续地滞后,造成需要 物理删除 的数据越来越多,使得表空间越来越大,磁盘的 purge 工作将使得一切都很慢。可以通过修改 innodb_max_purge_lag 配置项,强制 InnoDB 减速,以保证 purge 能跟得上。

二、 multi-version 与二级索引 secondary indexes

1. 区别

InnoDB 的多版本并发控制 MVCC - Multi-Version Concurrency Controll 对于聚簇索引 clustered index 和二级索引 secondary indexes 将会区别对待:

  • 聚簇索引 clustered index 中的记录会马上更新,对应地,这些记录指向 早期版本 回滚日志的隐藏字段(DB_TRX_IDDB_ROLL_PTRDB_ROW_ID)也会马上重建。
  • 而二级索引 secondary indexes 中不包含隐藏字段(DB_TRX_IDDB_ROLL_PTRDB_ROW_ID),也不会马上更新。
2. 二级索引 secondary indexes 的更新
  • 当二级索引 secondary indexes 的列被更新时,老的二级索引记录会做 删除标识,然后会插入新行,最后二级索引中记录的 删除标识 会被删除。
  • 如果二级索引 secondary indexes 的记录被做 删除标识 或者一个更新的事务对二级索引页 secondary indexes page 进行更新时:
    • InnoDB 会查找表的聚簇索引 clustered index 中的记录:在聚簇索引 clustered index 中,检查记录的 DB_TRX_ID,如果记录在只读事务开始后被修改了,InnoDB 会根据回滚日志将正确的记录构造出来 retrieved
    • 同时索引覆盖 covering index 功能不再支持:InnoDB 会从聚簇索引 clustered index 中查找记录,而不是直接使用索引中的数据。
    • 不过,如果开启了 index condition pushdown (ICP)(MySQL 用索引去表里取数据的一种)优化,并且部分 where 条件可以从索引中的字段进行评估,MySQL 依然会将这些 使用索引中字段评估过的部分 where 条件 下发到存储引擎。此时:
      • 如果存储引擎没有拿到结果,从聚簇索引 clustered index 中查找记录的过程会被省略。
      • 如果存储引擎找到了结果,甚至包含了已经在二级索引 secondary indexes 中被做 删除标识 的记录,InnoDB 会查找表的聚簇索引 clustered index 中的记录。

你可能感兴趣的:([翻译] MySQL 之 InnoDB 中的 Multi-Version 特性)