前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL 核心模块揭秘 | 09 期 | 二阶段提交 (3) flush、sync、commit 子阶段

MySQL 核心模块揭秘 | 09 期 | 二阶段提交 (3) flush、sync、commit 子阶段

作者头像
爱可生开源社区
发布2024-03-18 20:48:17
740
发布2024-03-18 20:48:17
举报

作者:操盛春,爱可生技术专家,公众号『一树一溪』作者,专注于研究 MySQL 和 OceanBase 源码。

爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源

本文基于 MySQL 8.0.32 源码,存储引擎为 InnoDB。

正文

1. 写在前面

经过上一篇文章的介绍,我们已经对 commit 阶段有了整体的认识。

这篇文章,我们一起进入各子阶段,看看它们都会干点什么,以及会怎么干。

为了方便理解,我们假设有 30 个事务,它们对应的用户线程编号也从 1 到 30。

2. flush 子阶段

用户线程 16 加入 flush 队列,成为 flush 队长,并且通过申请获得 LOCK_log 互斥量。

flush 队长收编用户线程 17 ~ 30 作为它的队员,队员们进入 flush 队列之后,就开始等待,收到 commit 子阶段的队长发来的通知才会结束等待。

flush 队长开始干活之前,会带领它的队员从 flush 队列挪出来,给后面进入二阶段提交的其它事务腾出空间。

从 flush 队列挪出来之后,flush 队长会触发操作系统,把截止目前产生的所有 redo 日志都刷盘。

这些 redo 日志,当然就包含了它和队员们在 prepare 阶段及之前产生的所有 redo 日志了。

触发 redo 日志刷盘之后,flush 队长会从它自己开始,把它和队员们产生的 binlog 日志写入 binlog 日志文件。

以队长为例,写入过程是这样的:

  • 从事务对应的 trx_cache 中把 binlog 日志读出来,存放到 trx_cache 的内存 buffer 中。 每次读取 4096(对应代码里的 IO_SIZE)字节的 binlog 日志,最后一次读取剩余的 binlog 日志(小于或等于 4096 字节)。
  • 把 trx_cache 内存 buffer 中的 binlog 日志写入 binlog 日志文件。

队员们产生的 binlog 日志写入 binlog 日志文件的过程,和队长一样。队长把自己和所有队员产生的 binlog 日志都写入 binlog 日志文件之后,flush 子阶段的活就干完了。

flush 队长写完 binlog 日志之后,如果发现 binlog 日志文件的大小大于等于系统变量 max_binlog_size 的值(默认为 1G),会设置一个标志(rotate = true),表示需要切换 binlog 日志文件。后面 commit 子阶段会用到。

到这里,用户线程 16 作为队长的 flush 子阶段,就结束了。

3. sync 子阶段

为了剧情需要,我们假设用户线程 6 ~ 15 此刻还在 sync 队列中,用户线程 6 最先进入队列,是 sync 队长,用户线程 7 ~ 15 都是队员。

用户线程 16(flush 队长)带领队员们来到 sync 子阶段,发现 sync 队列中已经有先行者了。

有点遗憾,用户线程 16 不能成为 sync 子阶段的队长,它和队员们都会变成 sync 子阶段的队员。

此时,用户线程 6 是 sync 队长,用户线程 7 ~ 30 是队员。

进入 sync 子阶段之后,用户线程 16(flush 队长)会释放它在 flush 子阶段获得的 LOCK_log 互斥量,flush 子阶段下一屇的队长就可以获得 LOCK_log 互斥量开始干活了。

交待完用户线程 16(flush 队长)在 sync 子阶段要干的活,该说说 sync 队长了。

sync 队长会申请 LOCK_sync 互斥量,获得互斥量之后,就开始准备给自己和队员们干 sync 子阶段的活了。

队员们依然在一旁当吃瓜群众,等待 sync 队长给它们干活。它们会一直等待,收到 commit 子阶段的队长发来的通知才会结束等待。

就在 sync 队长准备甩开膀子大干一场时,它发现前面还有一个关卡:本次组提交能不能触发操作系统把 binlog 日志刷盘

sync 队长怎么知道自己能不能过这一关?

它会查看一个计数器的值(sync_counter),如果 sync_counter + 1 大于等于系统变量 sync_binlog 的值,就说明自己可以过关。

否则,不能通过这一关,用户线程 6 作为队长的 sync 子阶段就到此结束了,它什么都不用干。

如果 sync 队长过关了,它会想:好不容易过关,我要再收编一些队员,才不枉费我的好运气。

sync 队长会带领队员们继续在 sync 队列中等待,以收编更多队员。这个等待过程是有期限的,满足以下两个条件之一,就结束等待:

  • 已经等待了系统变量 binlog_group_commit_sync_delay 指定的时间(单位:微妙),默认值为 0。
  • sync 队列中的用户线程数量(sync 队长和所有队员加在一起)达到了系统变量 binlog_group_commit_sync_no_delay_count 的值,默认值为 0。

等待结束之后,sync 队长会带领队员们从 sync 队列挪出来,给后面进入二阶段提交的其它事务腾出空间。

接下来,sync 队长终于可以大干一场了,它会触发操作系统把 binlog 日志刷盘,确保它和队员们产生的 binlog 日志写入到磁盘上的 binlog 日志文件中。

这样即使服务器突然异常关机,binlog 日志也不会丢失了。

刷盘完成之后,用户线程 6 作为队长的 sync 子阶段,就到此结束。

介绍完 sync 子阶段的主要流程,我们再来说说 sync_counter

sync_counter 的值从 0 开始,某一次组提交的 sync 队长没有过关,不会触发操作系统把 binlog 日志刷盘,sync_counter 就加 1。

sync_counter 会一直累加,直到后续的某一次组提交,sync_counter + 1 大于等于系统变量 sync_binlog 的值,sync 队长会把 sync_counter 重置为 0,并且触发操作系统把 binlog 日志刷盘。

sync_counter 重置为 0 之后,sync 子阶段是否要触发操作系统把 binlog 日志刷盘,又会开始一个新的轮回。

4. commit 子阶段

同样,为了剧情需要,我们假设用户线程 1 ~ 5 此刻还在 commit 队列中,用户线程 1 最先进入队列,是 commit 队长,用户线程 2 ~ 5 都是队员。

用户线程 6(sync 队长)带领队员们来到 commit 子阶段,发现 commit 队列中也已经有先行者了。

用户线程 6 和队员们一起,都变成了 commit 子阶段的队员。

此刻,用户线程 1 是 commit 队长,用户线程 2 ~ 30 是队员。

进入 commit 子阶段之后,用户线程 6(sync 队长)会释放它在 sync 子阶段获得的 LOCK_sync 互斥量,sync 子阶段下一屇的队长就可以获得 LOCK_sync 互斥量开始干活了。

交待完用户线程 6(sync 队长)进入 commit 子阶段要干的活,该说说 commit 队长了。

commit 队长会申请 LOCK_commit 互斥量,获得互斥量之后,根据系统变量 binlog_order_commits 的值决定接下来的活要怎么干。

如果 binlog_order_commits = true,commit 队长会把它和队员们的 InnoDB 事务逐个提交,然后释放 LOCK_commit 互斥量。

提交 InnoDB 事务完成之后,commit 队长会通知它的队员们(用户线程 2 ~ 30):所有活都干完了,你们都散了吧,别围观了,该干啥干啥去。

队员们收到通知之后,作鸟兽散,它们的二阶段提交也都结束了。

如果 binlog_order_commits = false,commit 队长不会帮助队员们提交 InnoDB 事务,它提交自己的 InnoDB 事务之后,就会释放 LOCK_commit 互斥量。

然后,通知所有队员(用户线程 2 ~ 30):flush 子阶段、sync 子阶段的活都干完了,你们自己去提交 InnoDB 事务。

队员们收到通知之后,就各自提交自己的 InnoDB 事务,谁提交完成,谁的二阶段提交就结束了。

最后,commit 队长还要处理最后一件事。

如果用户线程 16(flush 队长)把 rotate 设置为 true 了,说明 binlog 日志文件已经达到了系统变量 max_binlog_size 指定的上限,需要切换 binlog 日志文件。

切换指的是关闭 flush 子阶段刚写入的 binlog 日志文件,创建新的 binlog 日志文件,以供后续事务提交时写入。

如果需要切换 binlog 日志文件,切换之后,还会根据系统变量 binlog_expire_logs_auto_purgebinlog_expire_logs_secondsexpire_logs_days 清理过期的 binlog 日志。

处理完切换 binlog 日志文件的逻辑之后,commit 队长的工作就此结束,它的二阶段提交就完成了。

5. 总结

flush 子阶段,flush 队长会把自己和队员在 prepare 阶段及之前产生的 redo 日志都刷盘,把事务执行过程中产生的 binlog 日志写入 binlog 日志文件。

sync 子阶段,如果 sync_counter + 1 大于等于系统变量 max_binlog_size 的值,sync 队长会把 binlog 日志刷盘。

commit 子阶段,如果系统变量 binlog_order_commits 的值为 true,commit 队长会把自己和队员们的 InnoDB 事务都提交,否则,commit 队长和队员各自提交自己的 InnoDB 事务。

本期问题:commit 子阶段这种清理过期 binlog 日志的逻辑,会有什么问题吗?欢迎留言交流。

下期预告:MySQL 核心模块揭秘 | 10 期 | binlog 怎么写入日志文件?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-03-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱可生开源社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 写在前面
  • 2. flush 子阶段
  • 3. sync 子阶段
  • 4. commit 子阶段
  • 5. 总结
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档