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>