MySQL 核心模块揭秘 | 11 期 | InnoDB 提交事务,提交了什么?

MySQL 核心模块揭秘 | 11 期 | InnoDB 提交事务,提交了什么?

目录

  • 1. 关于缓存 undo 段

  • 2. InnoDB 提交事务

    • 2.1 修改 insert undo 段状态

    • 2.2 生成事务提交号

    • 2.3 修改 update undo 段状态

    • 2.4 undo 日志组加入 history list

  • 3. InnoDB 提交事务完成

  • 4. 重新初始化事务对象

  • 5. 总结

正文

1. 关于缓存 undo 段

为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。

只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。

条件 1:undo 段中只有一个 undo 页。

条件 2:这个唯一的 undo 页中,已经使用的的空间必须小于数据页大小的四分之三。以默认大小 16K 的 undo 页为例,undo 页中已经使用的空间必须小于 12K。

如果 insert undo 段满足缓存条件,它会加入回滚段的 insert_undo_cached 链表头部。

如果 update undo 段满足缓存条件,它会加入回滚段的 update_undo_cached 链表头部。

2. InnoDB 提交事务

二阶段提交过程中,commit 阶段的 flush 子阶段,把 prepare 阶段及之前产生的 redo 日志都刷盘了,把事务执行过程中产生的 binlog 日志都写入 binlog 日志文件了。

sync 子阶段根据系统变量 sync_binlog 的值决定是否触发操作系统把 binlog 日志刷盘。

前两个子阶段,都只处理了日志,不涉及 InnoDB 的事务。这两个阶段完成之后,InnoDB 的事务还没有提交,事务还处于准备提交状态(TRX_STATE_PREPARED
)。

commit 子阶段才会真正提交 InnoDB 的事务,这个阶段完成之后,事务就提交完成了。

commit 子阶段提交 InnoDB 的事务,要做的事情有这些:

  • 修改 insert undo 段的状态。
  • 生成事务提交号,用于 purge 线程判断是否能清理某些 update undo 日志组中的 undo 日志。
  • 修改 update undo 段的状态。
  • 把 update undo 段中的 undo 日志组加入回滚段的 history list 链表。purge 线程会从这个链表中获取需要清理的 update undo 日志组。
  • 把事务状态修改为 TRX_STATE_COMMITTED_IN_MEMORY
  • 释放事务执行过程中 InnoDB 给表或记录加的锁。
  • 重新初始化事务对象,以备当前线程后续使用。

2.1 修改 insert undo 段状态

如果事务插入记录到用户普通表,InnoDB 会为事务分配一个 insert undo 段。

如果事务插入记录到用户临时表,InnoDB 会为事务分配另一个 insert undo 段。

InnoDB 可能会给事务分配 0 ~ 2 个 insert undo 段。commit 子阶段会修分配给事务的所有 insert undo 段的状态。

如果 insert undo 段满足缓存条件,它的状态会被修改为 TRX_UNDO_CACHED
,否则,它的状态会被修改为 TRX_UNDO_TO_FREE

事务提交完成之后,InnoDB 会根据状态缓存或者释放 insert undo 段。

2.2 生成事务提交号

事务提交号是事务对象的 no
属性,通常用 trx->no
表示。

代码里,对事务提交号的注释是 transaction serialization number
,直译成中文应该称为事务序列号,或者事务串行号。

因为 trx->no 是在事务提交时生成的,我们还是把它称为事务提交号更容易理解一些。

只有 update undo 段需要事务提交号。purge 线程清理 update undo 日志时,会根据 update undo 段的 undo 日志组中保存的事务提交号,决定是否能清理这个 undo 日志组中的 undo 日志。

修改 update undo 段的状态之前,InnoDB 会生成事务提交号,保存到事务对象的 no
属性中。

// storage/innobase/trx/trx0trx.cc<br>static inline bool trx_add_to_serialisation_list(trx_t *trx) {<br>  ...<br>  trx->no = trx_sys_allocate_trx_no();<br>  ...<br>}<br>