MySQL 核心模块揭秘,你看明白了吗?

为了提升分配 undo 段的效率 , 事务提交过程中,InnoDB 会缓存一些 undo 段 。只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存 。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) 。
【MySQL 核心模块揭秘,你看明白了吗?】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.ccstatic inline bool trx_add_to_serialisation_list(trx_t *trx) {...trx->no = trx_sys_allocate_trx_no();...}trx_sys_allocate_trx_no() 调用 trx_sys_allocate_trx_id_or_no() 生成事务提交号 。
// storage/innobase/include/trx0sys.ic// 生成事务 IDinline trx_id_t trx_sys_allocate_trx_id() {ut_ad(trx_sys_mutex_own());return trx_sys_allocate_trx_id_or_no();}// 生成事务提交号inline trx_id_t trx_sys_allocate_trx_no() {ut_ad(trx_sys_serialisation_mutex_own());return trx_sys_allocate_trx_id_or_no();}从上面的代码可以看到,生成事务 ID 和事务提交号调用的是同一个方法,trx_sys_allocate_trx_id_or_no() 的代码如下:
// storage/innobase/include/trx0sys.icinline trx_id_t trx_sys_allocate_trx_id_or_no() {...// trx_sys_allocate_trx_id_or_no() 每次被调用// trx_sys->next_trx_id_or_no 加 1// trx_id 保存的是加 1 之前的值trx_id_t trx_id = trx_sys->next_trx_id_or_no.fetch_add(1);...return trx_id;}


推荐阅读