组提交 (group commit) 是为了优化写日志时的刷磁盘问题,从最初只支持 InnoDB redo log 组提交,到 5.6 官方版本同时支持 redo log 和 binlog 组提交,大大提高了 MySQL 的事务处理性能。
下面将以 InnoDB 存储引擎为例,详细介绍组提交在各个阶段的实现原理。
简介
自 5.1 之后,binlog 和 innodb 采用类似两阶段提交的方式,不过不支持 group commit;在 5.6 中,将 binlog 的 commit 阶段分为三个阶段:flush stage、sync stage 以及 commit stage。
这三个阶段中,每个阶段都会去维护一个队列,各个列表的定义如下。
Mutex_queue m_queue[STAGE_COUNTER];
如上,每个阶段都在维护一个队列,第一个进入该队列的作为 leader 线程,否则作为 follower 线程;leader 线程会收集 follower 的事务,并负责做 sync,follower 线程等待 leader 通知操作完成。
尽管维护了三个队列,但队列中所有的 THD 实际上都是通过 next_to_commit 连接起来。binlog 在事务提交阶段,也就是在 MYSQL_BIN_LOG::ordered_commit() 函数中,开始 3 个阶段的流程。
接下来,看看 MySQL 中事务是如何提交的。
事务提交
接下来,看看 InnoDB 和 binlog 提交的流程。
二阶段提交
详细介绍下二阶段提交的过程。
未开启binlog时
InnoDB 通过 redo 和 undo 日志来恢复数据库 (safe crash recovery),当数据恢复时,通过 redo 日志将所有已经在存储引擎内部提交的事务应用 redo log 恢复,所有已经 prepared 但是没有 commit 的事务则会通过 undo log 做回滚。
然后客户端连接时就能看到已经提交的数据存在数据库内,未提交被回滚地数据需要重新执行。
开启binlog时
为了保证存储引擎和 MySQL 的 binlog 保持一致,引入二阶段提交 (two phase commit, 2pc) 。
因为备库通过 binlog 重放主库提交的事务,假设主库存储引擎已经提交而 binlog 没有保持一致,则会使备库数据丢失造成主备数据不一致。
二阶段提交
如下是二阶段提交流程。