原文:https://jfg-mysql.blogspot.com/2025/06/interesting-troubleshooting-crash-filling-then-freeing-disk.html
最近知名MySQL博客J-F Gagné's 发了一篇关于MySQL bug的文章,题目是 Interesting Troubleshooting of a MySQL Crash : filling then freeing the disk 。
MySQL 崩溃场景总结 核心问题是,一个大型事务在提交过程中耗尽了磁盘空间,导致 MySQL 崩溃。
一个大型事务开始执行,需要产生大量的二进制日志(binary log)数据。对于超大事务,MySQL 的默认行为是将这部分数据“溢出”到磁盘上的一个临时文件,而不是全部保存在内存中。这种行为解释了您在第一张图表中观察到的,在超过 60 分钟的时间里,数十 GiB 磁盘空间被缓慢持续消耗的现象。
当事务准备提交时,MySQL 需要完成二进制日志的写入。在标准的 MySQL 实现中,这个过程需要通过复制临时文件来创建最终的二进制日志文件。这意味着服务器实际上需要临时文件两倍的磁盘空间来完成提交。正是这个操作导致了磁盘被迅速填满,并引发了致命错误。
崩溃
由于磁盘已满,服务器无法将二进制日志刷新(flush)到最终文件,导致刷新阶段失败。由于 binlog_error_action 被设置为 ABORT_SERVER,MySQL 会主动崩溃以防止数据不一致。崩溃后,临时二进制日志文件被立即删除,这解释了图表中数十 GiB 磁盘空间被迅速释放的现象。
崩溃后的恢复挑战 问题并未随着崩溃结束。恢复过程也暴露了进一步需要改进的地方:
回滚延迟
服务器重启后,必须执行崩溃恢复。这包括回滚那个巨大的、未提交的事务,以确保数据完整性。您的日志摘录清楚地显示,从 InnoDB 初始化结束(14:14)到 XA 崩溃恢复完成(42:50)之间存在巨大的时间间隔。这段时间就是用于回滚大型事务所花费的时间。在此漫长的回滚期间,服务器基本处于无响应状态,没有提供任何进度更新,您已经为此提出了 Bug #118358。
二进制日志清理
部分事务也必须从二进制日志文件中清理掉,以确保日志文件处于一致状态。您的扩展图表显示了另一次较小的磁盘空间释放,您认为这是清理二进制日志所致。
您提出的解决方案和 Bug 报告 您敏锐地指出了几个关键点和 MySQL 潜在的改进方向:
Bug #118356:MySQL 尝试提交不适合磁盘的事务,然后崩溃。
您认为 MySQL 应该更智能。它应该在开始提交前就检查是否有足够的可用磁盘空间,从而从一开始就避免崩溃。您承认修复此错误将引入一种提交失败的新场景,这在 MySQL 中是很少见的。
Bug #118358:回滚大事务时,启动过程没有进度迹象。
强调了恢复过程中糟糕的用户体验。服务器看起来像是卡住了,导致管理员感到不确定。您提到,某个 MySQL 分支对此有一个巧妙的解决方案,即在后台执行回滚,从而让服务器能更快地接受连接。
您以一个务实的最佳实践作为总结:虽然通常应避免使用大型事务,但 MySQL 对它们的处理应该更稳健。认为,从根本上避免崩溃(Bug #118356)和消除提交时对额外磁盘空间的需求(Bug #118332),是比仅仅改善回滚过程的日志输出更有价值的投资。
翻译有趣的 MySQL 崩溃排查:磁盘被填满然后又被释放
我最近排查了一个有趣的 MySQL 崩溃问题,觉得值得分享(以及相关的 Bug)。MySQL 在磁盘满的情况下崩溃了,下图展示了磁盘空闲空间的变化曲线:
Y 轴:以 GiB 为单位;
X 轴:以小时为单位。

你能猜到发生了什么吗?
为了确保我们对这张图的理解一致,先解释一下图的含义: 起初,磁盘有几十 GiB 的空闲空间;
然后,在 60 多分钟内,空闲空间被缓慢消耗掉一半以上紧接着磁盘被迅速填满(这部分没有在图中明确显示,此时 MySQL 崩溃)最后,磁盘空间迅速被释放(在崩溃时或刚崩溃之后)。
如果你还不能确定发生了什么,再补充一个可能不那么明显的线索崩溃后释放的磁盘空间数量,正好与之前缓慢消耗的空间一致。还是不确定?那请查看我上一篇文章:
日志总是藏着真相,来看一下当时的错误日志:
2025-xx-yyTzz:12:18.469308Z 1916497 [ERROR] [MY-011072] [Server] Binary logging not possible. Message: An error occurred during flush stage of the commit. 'binlog_error_action' is set to 'ABORT_SERVER'. Server is being stopped.
现在你大概已经猜出来了是一个大事务导致了大量的二进制日志生成这些 binlog 填满了磁盘空间,最终导致了 MySQL 的崩溃,不过在原版 MySQL(而非已做优化的分支中),二进制日志写入其实需要“双倍磁盘空间”!
上图中最后释放的磁盘空间,正好是为写 binlog 时溢出到临时文件而使用的空间。
为什么需要“双倍空间”?(提示:Binlog 写入机制) 如果你还不太理解上述现象,请阅读我上一篇博客,里面解释了大事务的 binlog 是如何写入的(先写临时文件,再复制),以及这如何导致空间占用翻倍。
需要指出的是,如果使用了上一篇文章中所提到的优化方案(在某个 MySQL 分支中),本次崩溃是完全可以避免的。
又一个“COMMIT 失败”的场景!
我认为,MySQL 在启动提交这样的大事务时,应该意识到自己“正在把自己逼入死角”——也就是会把磁盘填满,为此,我提交了 Bug 报告:
🐛 Bug #118356:MySQL 在提交事务时未检查磁盘空间是否足够,最终导致崩溃。
修复这个 Bug 之后,会带来一种 MySQL 以前没有的情况:COMMIT 可能失败。 以前我只见过 COMMIT 在 Group Replication 的冲突检测中失败。
如何复现这个崩溃? 复现步骤详见上述 Bug 报告和附录 #1,其中包含了观察 binlog 临时文件大小、剩余磁盘空间等命令。但问题还没完:崩溃后的恢复很慢! 崩溃之后,MySQL 并不会像“魔术一样”顺利启动。
未提交的大事务必须回滚部分写入的事务日志需要从 binlog 中清理 这两项操作都很耗时。我们可以通过扩展版的磁盘空间图观察到回滚延迟和 binlog 清理过程。日志片段也反映了这个延迟过程。

2025-xx-yyTzz:12:18.469308Z 1916497 [ERROR] Binary logging not possible... ABORT_SERVER... ... 2025-xx-yyTzz:12:20.297403Z [InnoDB] InnoDB initialization has started. 2025-xx-yyTzz:14:14.898222Z [InnoDB] InnoDB initialization has ended. 2025-xx-yyTzz:16:02.120982Z [Server] Starting XA crash recovery... 2025-xx-yyTzz:42:50.287434Z [Server] XA crash recovery finished.
我们能看到日志中存在几个小时的“时间空白”——这也促使我提交了另一个 Bug:
🐛 Bug #118358:当回滚大事务时,MySQL 启动日志中没有任何进度提示,用户无法判断当前系统是否卡住。
某个 MySQL 分支的创意改进 该分支(由阿里云工程师宋利兵贡献)引入了一种创新方式优化恢复流程,不再等待大事务回滚完成才启动数据库,而是后台异步回滚事务,同时让 Server 提前对外提供连接服务!
他们在博客中详细介绍了这个优化(Rollback Prepared Transactions Asynchronously During Binlog Crash Recovery),我也在 LinkedIn 上发表了我的看法。
这个优化对 MySQL 来说是有价值的,但对我来说还不是最高优先级。我更希望优先修复其他问题(比如 Bug #118332 中的 binlog 空间使用优化),因此我没有为这个功能提出 Feature Request。
其实,MySQL完全可以不崩溃!既然崩溃后能恢复,不如“直接恢复而不崩溃”
在提交前就拒绝这个事务(Bug #118356)消除提交时额外占用空间的需求(Bug #118332)结尾提醒
通常我们认为,MySQL 最佳实践是避免使用大事务。虽然我建议优化 binlog 写入过程以适配大事务,但这并不表示我鼓励使用大事务。我仍然建议:事务应小于 10 MiB。无论是否遵循此最佳实践,MySQL 都应该让大事务的提交更快、更节省资源。
📎 附录 #1:因磁盘空间不足导致 binlog 拷贝失败的复现实验
实验环境
AWS 实例:m6id.large(2 vCPU,8 GiB RAM,118 GiB 本地 SSD)
使用 dbdeployer 构建沙箱构造 1 亿行、每行约 4 KiB 的数据更新语句会阻塞,然后因 binlog 空间不足失败。
本文分享自 AustinDatabases 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!