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

trx_sys->next_trx_id_or_no 保存的是下一个事务 ID 或事务提交号,具体是哪个,取决于是生成事务 ID 还是生成事务提交号先调用 trx_sys_allocate_trx_id_or_no() 。
也就是说,事务 ID 和事务提交号是同一条流水线上生产出来的 。我们以 trx 1 和 trx 2 两个事务为例,来说明生成事务 ID 和事务提交号的流程 。
假设此时 trx_sys->next_trx_id_or_no 的值为 100,trx 1、trx 2 启动和提交的顺序如下:

  • trx 1 启动 。
  • trx 2 启动 。
  • trx 1 提交 。
  • trx 2 提交 。
其于以上假设,生成事务 ID 和事务提交号的流程如下:
  • trx 1 生成事务 ID,得到 100 。trx_sys->next_trx_id_or_no 加 1 , 结果为 101 。
  • trx 2 生成事务 ID , 得到 101 。trx_sys->next_trx_id_or_no 加 1,结果为 102 。
  • trx 1 生成事务提交号,得到 102 。trx_sys->next_trx_id_or_no 加 1,结果为 103 。
  • trx 2 生成事务提交号,得到 103 。trx_sys->next_trx_id_or_no 加 1,结果为 104 。
从以上流程可以看到,事务 ID 和事务提交号都来源于 trx_sys->next_trx_id_or_no , 相互之间不会重复 。
2.3 修改 update undo 段状态如果事务更新或删除了用户普通表的记录,InnoDB 会为事务分配一个 update undo 段 。
如果事务更新或删除了用户临时表的记录,InnoDB 会为事务分配另一个 update undo 段 。
InnoDB 可能会给事务分配 0 ~ 2 个 update undo 段 。commit 子阶段会修改分配给事务的所有 update undo 段的状态 。
如果 update undo 段满足缓存条件 , 它的状态会被修改为 TRX_UNDO_CACHED,否则,它的状态会被修改为 TRX_UNDO_TO_PURGE 。
2.4 undo 日志组加入 history list修改完 update undo 段的状态,update undo 段的 undo 日志组会加入回滚段的 history list 链表 。purge 线程会从这个链表中获取要清理的 undo 日志组 。
前面已经生成了事务提交号,这里会把事务提交号写入 undo 日志组的头信息中 。
如果 update undo 段的状态为 TRX_UNDO_CACHED , 表示这个 undo 段需要缓存起来 。它会加入回滚段的 update_undo_cached 链表头部 , 以备后续其它事务需要 update undo 段时,能够快速分配 。
3. InnoDB 提交事务完成前面的一系列操作完成之后,InnoDB 提交事务的操作就完成了 。
现在,要把事务状态修改为 TRX_STATE_COMMITTED_IN_MEMORY 。
修改之后,新启动的事务就能看到该事务插入或更新的记录,看不到当前事务删除的记录 。
接下来,InnoDB 会释放事务执行过程中加的表锁、记录锁 。
释放锁之后,还要处理 insert undo 段 。
如果 insert undo 段的状态为 TRX_UNDO_CACHED,表示这个 undo 段需要缓存起来 。它会加入回滚段的 insert_undo_cached 链表头部,以备后续其它事物需要 insert undo 段时,能够快速分配 。
如果 insert undo 段的状态为 TRX_UNDO_TO_FREE , 它会被释放,占用的 undo 页会还给 undo 表空间 。
二阶段提交的 flush 子阶段,已经把 prepare 阶段及之前产生的 redo 日志都刷盘了 。
commit 子阶段,修改 insert undo 段和 update undo 段的状态 , 还会产生 redo 日志 。
InnoDB 不会主动触发操作系统把这些 redo 日志刷盘,而是由操作系统决定什么时候把这些 redo 日志刷盘 。
InnoDB 敢这么做,是因为这些 redo 日志对于确定事务状态已经不重要了 。即使这些 redo 日志刷盘之前 , 服务器突然异常关机,导致 undo 段的状态丢失 。MySQL 下次启动时 , 也能正确的识别到事务已经提交完成了 。
4. 重新初始化事务对象到这里,InnoDB 提交事务该做的操作都已经做完了 。提交事务完成之后,该做的事也都做了 。
对于上一个事务 , 事务对象的使命已经结束 。这里会把事务状态修改为 TRX_STATE_NOT_STARTED 。
事务对象也会被重新初始化,但是它不会被释放 。也就是说,事务对象不会回到事务池中 , 而是留给当前连接后续启动新事务时复用 。
5. 总结InnoDB 提交事务,就像我们填完一个表格之后,最后盖上的那个戳,总体上来说,要干 3 件事 。
第 1 件,修改分配给事务的各 undo 段的状态 。


推荐阅读