InnoDB:InnoDB 多版本
关于
InnoDB是多版本存储引擎:它保留有关已更改行的旧版本的信息,以支持诸如并发和回滚的事务功能。此信息以称为“rollback segment”(回滚段)的数据结构存储在表空间中(在 Oracle 中类似的数据结构之后)。
- InnoDB 使用回滚段中的信息来执行事务回滚中所需的撤消操作。它还使用该信息为“consistent read”(一致读取)构建行的早期版本。
在内部,InnoDB 向存储在数据库中的每一行添加三个字段:【???】
- 6 字节的“DB_TRX_ID”字段指示插入或更新该行的最后一笔 Transaction 的 Transaction 标识符。此外,删除在内部被视为更新,在该更新中,该行中的特殊位被设置为将其标记为已删除。
- 每行还包含一个 7 字节的“DB_ROLL_PTR”字段,称为“滚动指针”。回滚指针指向写入回滚段的撤消日志记录。如果行已更新,则撤消日志记录将包含在更新行之前重建行内容所必需的信息。
- 一个 6 字节的“DB_ROW_ID”字段包含一个行 ID,该行 ID 随着插入新行而单调增加。如果 InnoDB 自动生成聚集索引,则该索引包含行 ID 值。否则,“DB_ROW_ID”列不会出现在任何索引中。
回滚段(rollback segment)中的撤消日志(undo log)分为插入和更新撤消日志:
- 插入撤消日志仅在事务回滚时才需要,并且在事务提交后可以立即将其丢弃。
- 更新撤消日志也用于一致的读取,但是只有在没有事务存在的情况下,InnoDB 才可以丢弃它们,因为 InnoDB 为该事务分配了一个快照,在一致读取中,该快照可能需要更新撤消日志中的信息来构建数据库行的早期版本。
- 定期提交您的 Transaction,包括仅发出一致读取的 Transaction。否则,InnoDB 无法丢弃更新撤消日志中的数据,并且回滚段可能会变得太大,从而填满了您的表空间。
- 回滚段中撤消日志记录的物理大小通常小于相应的插入或更新的行。您可以使用此信息来计算回滚段所需的空间。
在 InnoDB 多版本方案中,当您使用 SQL 语句删除行时,并不会立即将其从数据库中物理删除。 InnoDB 仅在丢弃为删除而编写的更新撤消日志记录时,才物理删除相应的行及其索引记录。此删除操作称为“purge”,它非常快,通常花费与执行删除操作的 SQL 语句相同的时间序列。
- 如果您以大约相同的速率在表中以较小的批次插入和删除行,则由于所有“死”行,清除线程可能会开始滞后并且表可能会变得越来越大,从而使所有内容都受磁盘约束慢。在这种情况下,请限制新行的操作,并通过调整“innodb_max_purge_lag”系统变量来向清除线程分配更多资源。
P.S. 相关概念:
- “多版本并发控制”(MVCC):
- “多版本并发控制”的缩写。这种技术可以让具有某些“隔离级别”(“REPEATABLE READ”、“READ COMMITTED”)的 InnoDB “Transaction”执行“一致的读取”(consistent read,即“非锁定读取”,也就是“快照读”,见“一致的非锁定读取”)操作;也就是说,查询其他事务正在更新的行,并查看发生这些更新之前的值。这是一种强大的技术,可通过允许查询 continue 进行而无需 await 由于其他事务持有的“锁”而增加“并发性”(concurrency)。
- 该技术在数据库领域并不普遍。其他一些数据库产品和其他一些 MySQL 存储引擎不支持它。
- “rollback segment”(回滚段):
- 包含“undo log”(撤消日志)的存储区。
- 回滚段传统上位于 system tablespace 中。从 MySQL 5.6 开始,回滚段可以驻留在 undo tablespace 中。从 MySQL 5.7 开始,回滚段也分配给 global temporary tablespace。
- “consistent read”(一致读取):
- 一种读取操作,该读取操作使用“快照”信息来基于某个时间点显示查询结果,而不管同时运行的其他事务执行的更改如何。如果查询的数据已被其他事务更改,则基于undo log的内容重建原始数据。通过强制事务 await 其他事务完成,该技术避免了某些可以减少“并发”的“锁定”问题。【!!!】
- 使用“REPEATABLE READ”隔离级别时,快照基于执行第一次读取操作的时间。【!】
- 在“READ COMMITTED”隔离级别下,快照将重置为每次一致读取操作的时间。【!】
- 一致读取是默认模式,其中 InnoDB 处理“READ COMMITTED”和“REPEATABLE READ”隔离级别中的“SELECT”语句。因为一致读取不会在它访问的表上设置任何锁,所以在对表执行一致读取时,其他会话可以自由地修改这些表。
- “并发性”(concurrency):
- 多个操作(在数据库术语中,称为“事务”)可以同时运行而不会互相干扰的能力。并发还与性能有关,因为理想情况下,使用有效的“锁定”机制,对多个并发事务的保护以最小的性能开销工作。
- “purge”:
- 一种垃圾收集,由一个或多个独立的后台线程(由“innodb_purge_threads”控制)执行,并定期执行。从“历史记录列 table”中清除分析并处理“撤消日志”页面,以删除标记为要删除的集群索引和二级索引记录(以前的“DELETE”语句),而对于 MVCC 则不再需要或 rollback 。清除日志后,将其从历史记录列表中释放出来。
多版本索引和二级索引【???】
InnoDB 多版本并发控制(MVCC)对二级索引的处理与对聚集索引的处理不同:
- 聚集索引中的记录将就地更新,其隐藏的系统列指向撤消日志条目,可以从中重建记录的早期版本。
- 二级索引记录不包含隐藏的系统列,也不会就地更新。
更新二级索引列时,将对旧的二级索引记录进行删除标记,插入新记录,并最终清除带有删除标记的记录。当二级索引记录被删除标记或二级索引页被更新的事务更新时,InnoDB 将在聚集索引中查找数据库记录。在聚集索引中,检查记录的“DB_TRX_ID”,如果在启动读取事务之后修改了记录,则从撤消日志中检索记录的正确版本。
如果将二级索引记录标记为删除或通过较新的事务更新了二级索引页,则不使用“covering index”(覆盖索引)技术。 InnoDB 不是从索引结构中返回值,而是在聚集索引中查找记录。
但是,如果启用了“索引条件下推”(ICP)优化,并且只能使用索引中的字段来评估“WHERE”条件的某些部分,则 MySQL 服务器仍会将“WHERE”条件的这一部分下推到使用索引对其进行评估的存储引擎。如果找不到匹配的记录,则避免聚集索引查找。如果找到匹配的记录,即使在删除标记的Logging,InnoDB 也会在聚集索引中查找记录。
P.S. 相关概念:
- “covering index”(覆盖索引):【???】
- 包含查询检索到的所有列的索引。该查询不使用索引值作为查找完整表行的指针,而是从索引结构返回值,从而节省了磁盘I/O。InnoDB 可以将这种优化技术应用到比 MyISAM 更多的索引中,因为 InnoDB 二级索引还包括主键列。在该事务结束之前,InnoDB不能将这种技术应用于对由事务修改的表的查询。
- 给定正确的查询,任何“列索引”或“复合索引”都可以充当覆盖索引。设计索引和查询以尽可能利用此优化技术。
- “索引条件下推”(ICP):
- 索引条件下推(ICP)是针对 MySQL 使用索引从表中检索行的情况的优化。如果没有 ICP,则存储引擎将遍历索引以在基表中定位行,并将其返回给 MySQL 服务器,该 MySQL 服务器将评估这些行的“WHERE”条件。启用 ICP 后,如果只能使用索引中的列来评估“WHERE”条件的一部分,则 MySQL 服务器会将“WHERE”条件的这一部分下推到存储引擎。然后,存储引擎通过使用索引条目来评估推送的索引条件,并且只有在满足此条件的情况下,才从表中读取行。
- ICP 可以减少存储引擎必须访问基表的次数以及 MySQL 服务器必须访问存储引擎的次数。【参见,MySQL 手册“优化”->“优化 SQL 语句”->“优化 SELECT 语句”->“索引条件下推式优化”】
- 聚集索引(Clustered Index,也称为聚簇索引、聚合索引、聚类索引、簇集索引):