故障分析 | binlog flush 失败导致的 Crash
数据库小白一枚,正在不断学习积累知识,现将学习到的知识记录一下,也是将我的所得分享给大家!而今天这篇文章《故障分析 | binlog flush 失败导致的 Crash》带大家来了解一下故障分析 | binlog flush 失败导致的 Crash,希望对大家的知识积累有所帮助,从而弥补自己的不足,助力实战开发!
作者:xuty
开个坑,记录自己平时由于解决问题需要或是兴趣研究进行的 MySQL 源码跟踪学习过程。
一、问题现象
某项目上出现
2019-09-24T14:17:47.786460+08:00 527621 [ERROR] /usr/local/mysql/bin/mysqld: Binary logging not possible. Message: An error occurred during flush stage of the commit. 'binlog_error_action' is set to 'ABORT_SERVER'. Hence aborting the server. 06:17:47 UTC - mysqld got signal 6 ; This could be because you hit a bug. It is also possible that this binary or one of the libraries it was linked against is corrupt, improperly built, or misconfigured. This error can also be caused by malfunctioning hardware. Attempting to collect some information that could help diagnose the problem. As this is a crash and something is definitely wrong, the information collection process might fail. key_buffer_size=8388608 read_buffer_size=16777216 max_used_connections=79 max_threads=2500 thread_count=35 connection_count=34 It is possible that mysqld could use up to key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 122921649 K bytes of memory Hope that's ok; if not, decrease some variables in the equation. Thread pointer: 0x7fbc3400ef40 Attempting backtrace. You can use the following information to find out where mysqld died. If you see no messages after this, something went terribly wrong... stack_bottom = 7fbcd01d9ea8 thread_stack 0x40000 /usr/local/mysql/bin/mysqld(my_print_stacktrace+0x35)[0xf4fbe5] /usr/local/mysql/bin/mysqld(handle_fatal_signal+0x4a4)[0x7d1f54] /lib64/libpthread.so.0(+0xf370)[0x7fc5897fe370] /lib64/libc.so.6(gsignal+0x37)[0x7fc5881f21d7] /lib64/libc.so.6(abort+0x148)[0x7fc5881f38c8] /usr/local/mysql/bin/mysqld[0xee626a] /usr/local/mysql/bin/mysqld(_ZN13MYSQL_BIN_LOG33handle_binlog_flush_or_sync_errorEP3THDb+0x17b)[0xef289b] /usr/local/mysql/bin/mysqld(_ZN13MYSQL_BIN_LOG14ordered_commitEP3THDbb+0x3ca)[0xef480a] /usr/local/mysql/bin/mysqld(_ZN13MYSQL_BIN_LOG6commitEP3THDb+0x51d)[0xef4f7d] /usr/local/mysql/bin/mysqld(_Z15ha_commit_transP3THDbb+0x174)[0x823434] /usr/local/mysql/bin/mysqld(_Z12trans_commitP3THD+0x49)[0xdd38f9] /usr/local/mysql/bin/mysqld(_Z21mysql_execute_commandP3THDb+0x2c89)[0xd1a1a9] /usr/local/mysql/bin/mysqld(_Z11mysql_parseP3THDP12Parser_state+0x40d)[0xd1c86d] /usr/local/mysql/bin/mysqld(_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command+0x11a5)[0xd1da95] /usr/local/mysql/bin/mysqld(_Z10do_commandP3THD+0x194)[0xd1e944] /usr/local/mysql/bin/mysqld(handle_connection+0x2b4)[0xdeffa4] /usr/local/mysql/bin/mysqld(pfs_spawn_thread+0x174)[0xfc8f64] /lib64/libpthread.so.0(+0x7dc5)[0x7f097d411dc5] /lib64/libc.so.6(clone+0x6d)[0x7f097becf73d] Trying to get some variables. Some pointers may be invalid and cause the dump to abort. Query (7f00a80008f0): is an invalid pointer Connection ID (thread ID): 98525 Status: NOT_KILLED
二、问题疑惑
对于这个问题,一开始存在几点疑惑,需要实验和源码追踪验证。
- binlog error 是由于 / 分区空间已满造成的,为什么会造成 MySQL Crash?
- / 分区明明有十几个 G 的剩余空间,为什么会满?是什么文件撑满的?为啥事后空间却自动释放了?
这里就已知的知识先猜测下问题过程,是否正确则需要测试验证,内部机制可能就需要去查看源码。

大概过程如上,项目上
#bin包默认值 datadir = /data/mysql_data tmpdir = /tmp
当
Filesystem Size Used Avail Use% Mounted on /dev/mapper/cl-root 96G 57G 39G 60% /
模拟 为了查看上述测试过程到底是在源码的哪一步最先报了错误,所以对比了 2 份 DBUG 日志,mysql> call test_insert();
ERROR 1026 (HY000): Error writing file '/data/tmp/ML1aShmH' (errno: 28 - No space left on device)
mysql> commit;
ERROR 1598 (HY000): Binary logging not possible. Message: An error occurred during flush stage of the commit. 'binlog_error_action' is set to 'ABORT_SERVER'. Hence aborting the server.
mysql> call test_insert();
ERROR 1026 (HY000): Error writing file '/data/tmp/ML5b0Mj6' (errno: 28 - No space left on device)
mysql> start transaction;
ERROR 1598 (HY000): Binary logging not possible. Message: An error occurred during flush stage of the commit. 'binlog_error_action' is set to 'ABORT_SERVER'. Hence aborting the server.
五、源码跟踪
跟踪 MySQL 源码验证上述猜想及测试,不感兴趣的童鞋可以直接跳过~
//Write a chunk of bytes to a file
size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags)
{
size_t writtenbytes;
size_t sum_written= 0;
const size_t initial_count= Count;
for (;;)
{
//...
#ifdef _WIN32
writtenbytes= my_win_write(Filedes, Buffer, Count);
#else
writtenbytes= write(Filedes, Buffer, Count); //writtenbytes为实际写入的字节数
//...
if (writtenbytes == Count) //判断实际写入的字节数与要求写入的字节数是否相符
{
//写入正常则直接break跳出循环,正常结束.
sum_written+= writtenbytes;
break;
}
//...
}
//...
if (MyFlags & (MY_NABP | MY_FNABP))
{
if (sum_written == initial_count) //写入正常走这个逻辑,返回0代表成功
DBUG_RETURN(0);
if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) //写入过程报错走这个逻辑
{
char errbuf[MYSYS_STRERROR_SIZE];
my_error(EE_WRITE, MYF(0), my_filename(Filedes),
my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno())); //打印错误信息
}
DBUG_RETURN(MY_FILE_ERROR); //返回错误
}
//...
}
/*
源码如上,MySQL最终会走Linux内核方法 write(Filedes, Buffer, Count)来刷新binlog缓冲至磁盘文件中。
1. Filedes 为文件号(对应/proc/mysqld_pid/fd下的文件编号)
2. Buffer 为指向所需写入缓冲的指针。
3. Count 为所需要写入的字节数。
my: fd: 51 Buffer: 0x7f24c49e9e30 Count: 27
由于 /data/tmp 磁盘已满,无法写入 Count 所需的字节数,导致 writtenbytes!=Count,然后引起了后续一系列的代码报错,最终诱发 binlog_error_action->MySQL Crash.
*/
MySQL 源码执行 Commit 报错过程精简如下:
T@12: | >trans_commit T@12: | | >ha_commit_trans T@12: | | | >MYSQL_BIN_LOG::commit T@12: | | | | >MYSQL_BIN_LOG::ordered_commit T@12: | | | | | >MYSQL_BIN_LOG::process_flush_stage_queue T@12: | | | | | | >binlog_cache_data::flush T@12: | | | | | | | >MYSQL_BIN_LOG::write_cache(THD *, binlog_cache_data *, bool) T@12: | | | | | | | | >MYSQL_BIN_LOG::do_write_cache T@12: | | | | | | | | | >reinit_io_cache T@12: | | | | | | | | | | >my_b_flush_io_cache T@12: | | | | | | | | | | | >my_write T@12: | | | | | | | | | | | | my: fd: 52 Buffer: 0x7f9ccc050db0 Count: 27 MyFlags: 20 T@12: | | | | | | | | | | | | error: Write only -1 bytes, error: 28 T@12: | | | | | | | | | | | | >my_error T@12: | | | | | | | | | | | | | >my_message_sql T@12: | | | | | | | | | | | | | | error: error: 3 message: 'Error writing file '/data/tmp/MLJRp8sa' (Errcode: 28 - No space left on device)' T@12: | | | | | | | | | | | | | <my_message_sql t binlog_cache_data bool>my_error T@12: | | | | | | >my_message_sql T@12: | | | | | | | error: error: 1598 message: 'Binary logging not possible. Message: An error occurred during flush stage of the commit. 'binlog_error_action' is set to 'ABORT_SERVER'. Hence aborting the server.' T@12: | | | | | | | >sql_print_error T@12: | | | | | | | | >error_log_print T@12: | | | | | | | | | >print_buffer_to_file T@12: | | | | | | | | | | enter: buffer: /opt/mysql-5.7.24-linux-glibc2.12-x86_64/bin/mysqld-debug: Binary logging not possible. Message: An error occurred during flush stage of the commit. 'binlog_error_action' is set to 'ABORT_SERVER'. Hence aborting the server. 2019-11-11T11:54:45.434783+08:00 12 [ERROR] /opt/mysql-5.7.24-linux-glibc2.12-x86_64/bin/mysqld-debug: Binary logging not possible. Message: An error occurred during flush stage of the commit. 'binlog_error_action' is set to 'ABORT_SERVER'. Hence aborting the server. T@12: | | | | | | | | | <print_buffer_to_file t></print_buffer_to_file></my_message_sql>
六、扩展内容
下图这个报错其实就是本次问题的一个日常例子,很多童鞋通过 navicate 还原 MySQL 的时候可能就会碰到这个问题,本质就是 navicate 还原数据库的时候默认会走
事务,当表数据库较大时,事务大小超过
binlog_cache_size,就会用临时表记录事务数据,当 tmdir 目录磁盘不足就会报错,但是这种情况并不会造成 MySQL Crash,因为 navicate 在还原报错后并不会继续去执行
commit或
另开一个事务,而是直接断开连接。


七、总结
这个问题目前在项目上很少碰到,这次也是出于好奇拿来学习探讨,下面总结下这个问题出现的场景:
1.代码中存在较大事务,超过 binlog_cache_size,高并发下生成大量临时文件,占满 tmpdir。
2.代码在事务执行过程中碰到 tmpdir 磁盘已满错误,未处理异常执行回滚,后续执行 Commit 导致。
3.代码在事务执行过程中碰到 tmpdir 磁盘已满错误,未处理异常执行回滚,继续执行碰到嵌套事务,引发 Commit 导致。
也许很多童鞋想到可以
加大binlog_cache_size来减少临时文件的产生,但是这样会增加内存消耗,试想以下,假如 binlog_cache_size 增加到
32MB,当有
300个数据库连接时,每个连接都会分配 32MB 的 binlog_cache(
不管你用多少),那么就是将近 10G,很容易导致内存溢出,被系统 OOM。
因此 binlog_cache_size 不可能增加很多,根本解决方法是减少事务大小,避免在高并发下同时产生大量临时文件,撑满 tmpdir;另外可以增加 tmpdir 所在分区的大小。
遗留问题:
CentOS Linux release 7.3.1611 问题复现成功,commit 会导致 Crash。
CentOS Linux release 7.6.1810 问题无法复现,commit 不报错,但是会造成 binlog event 部分写。
附录:
https://blog.51cto.com/lee90/...
https://dev.mysql.com/doc/ref...
https://stackoverflow.com/que...
今天带大家了解了MySQL、数据库的相关知识,希望对你有所帮助;关于数据库的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~
-
- 数据库 · MySQL | 2天前 |
- MySQL设置中文界面,超简单教程来了!
- 332浏览 收藏
-
- 数据库 · MySQL | 2天前 | mysql 索引提示
- MySQL进阶必看!FORCE/USE/IGNOREINDEX用法大揭秘
- 182浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- 手把手教你写MySQL存储过程,小白也能轻松上手
- 163浏览 收藏
-
- 数据库 · MySQL | 2天前 | mysql group by
- MySQL分组查询优化:GROUPBY原理+索引优化超全解析
- 324浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL设置中文语言,轻松拥有中文界面
- 211浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL建库语句从入门到精通:创建数据库+设置字符集&排序规则(附实例)
- 176浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- 从零开始学MySQL数据库操作,小白轻松变大神!
- 496浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL插入日期到时间字段,轻松搞定日期格式
- 484浏览 收藏
-
- 数据库 · MySQL | 2天前 | mysql 数据压缩
- MySQL怎么实现高效压缩存储?表压缩+列式存储详细解读
- 272浏览 收藏
-
- 数据库 · MySQL | 2天前 | mysql JOIN优化
- MySQL优化JOIN操作:七大技巧教你提升关联查询速度
- 106浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL出现中文乱码?超详细解决方案一次性搞定
- 211浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL主从复制这样配!搞懂这些参数,replication稳了~
- 131浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 茅茅虫AIGC检测
- 茅茅虫AIGC检测,湖南茅茅虫科技有限公司倾力打造,运用NLP技术精准识别AI生成文本,提供论文、专著等学术文本的AIGC检测服务。支持多种格式,生成可视化报告,保障您的学术诚信和内容质量。
- 26次使用
-
- 赛林匹克平台(Challympics)
- 探索赛林匹克平台Challympics,一个聚焦人工智能、算力算法、量子计算等前沿技术的赛事聚合平台。连接产学研用,助力科技创新与产业升级。
- 51次使用
-
- 笔格AIPPT
- SEO 笔格AIPPT是135编辑器推出的AI智能PPT制作平台,依托DeepSeek大模型,实现智能大纲生成、一键PPT生成、AI文字优化、图像生成等功能。免费试用,提升PPT制作效率,适用于商务演示、教育培训等多种场景。
- 59次使用
-
- 稿定PPT
- 告别PPT制作难题!稿定PPT提供海量模板、AI智能生成、在线协作,助您轻松制作专业演示文稿。职场办公、教育学习、企业服务全覆盖,降本增效,释放创意!
- 55次使用
-
- Suno苏诺中文版
- 探索Suno苏诺中文版,一款颠覆传统音乐创作的AI平台。无需专业技能,轻松创作个性化音乐。智能词曲生成、风格迁移、海量音效,释放您的音乐灵感!
- 60次使用
-
- golang MySQL实现对数据库表存储获取操作示例
- 2022-12-22 499浏览
-
- 搞一个自娱自乐的博客(二) 架构搭建
- 2023-02-16 244浏览
-
- B-Tree、B+Tree以及B-link Tree
- 2023-01-19 235浏览
-
- mysql面试题
- 2023-01-17 157浏览
-
- MySQL数据表简单查询
- 2023-01-10 101浏览