semi-sync插件崩溃导致MySQL重启的故障分析全过程
发表于:2025-01-23 作者:千家信息网编辑
千家信息网最后更新 2025年01月23日,导读semi-sync插件崩溃导致MySQL重启的故障分析全过程~ 作者:刘安 现为爱可生高级测试工程师,拥有丰富的自动测试开发经验;曾先后在文思海辉、Splunk担任测试工程师。一、起因:在公司测试
千家信息网最后更新 2025年01月23日semi-sync插件崩溃导致MySQL重启的故障分析全过程导读
一、起因:
1、OS: CentOS release 6.7 (Final); 2、MySQL:Ver 14.14 Distrib 5.7.13, for linux-glibc2.5 (x86_64) using EditLine wrapper; 3、主从实例开启半同步 MySQL semi-sync配置文档。
所以升级MySQL版本可以解决问题。
最后总结一下我的诊断路径:
semi-sync插件崩溃导致MySQL重启的故障分析全过程~ 作者:刘安 现为爱可生高级测试工程师,拥有丰富的自动测试开发经验;曾先后在文思海辉、Splunk担任测试工程师。
一、起因:
在公司测试MySQL高可用组件时发现一个异常。如下:
停止从库,高可用组件将从库自动启动后,主库发生了重启。然而,正常情况下,主库不应发生重启。二、环境:
1、OS: CentOS release 6.7 (Final); 2、MySQL:Ver 14.14 Distrib 5.7.13, for linux-glibc2.5 (x86_64) using EditLine wrapper; 3、主从实例开启半同步 MySQL semi-sync配置文档。
三、分析:
(一)首先,分析log文件:
在主库的mysql-error.log中,我们可以发现如下几点: 1、主库的semi-sync在崩溃前做了启停动作: 2017-07-25T16:02:38.636061+08:00 40 [Note] Semi-sync replication switched OFF. 2017-07-25T16:02:38.636105+08:00 40 [Note] Semi-sync replication disabled on the master. 2017-07-25T16:02:38.636137+08:00 0 [Note] Stopping ack receiver thread 2017-07-25T16:02:38.638008+08:00 40 [Note] Semi-sync replication enabled on the master. 2017-07-25T16:02:38.638131+08:00 0 [Note] Starting ack receiver thread 2、主库的semi-sync启动后,立即引发了semi-sync插件崩溃并抛出Assert异常。 mysqld: /export/home/pb2/build/sb_0-19016729-1464157976.67/mysql-5.7.13/plugin/semisync/semisync_master.cc:844: int ReplSemiSyncMaster::commitTrx(const char*, my_off_t): Assertion `entry' failed. 08:02:38 UTC - mysqld got signal 6 ; 3、这里还有一个相当重要的复现故障的线索。简单说明一下,这条SQL语句是为了检测主从数据差,而由高可用中间件不断地向数据库更新时间戳。 Trying to get some variables. Some pointers may be invalid and cause the dump to abort. Query (7f408c0054c0): update universe.u_delay set real_timestamp=now(), logic_timestamp = logic_timestamp + 1 where source = 'ustats' Connection ID (thread ID): 61 Status: NOT_KILLED 到这里初步判断是这个故障应与设置rpl_semi_sync_master_enabled开关相关,并且应该当时是有事务提交的。(二)其次,进一步验证:
这里只部署一组开启semi-sync的MySQL实例,而不安装高可用组件,利用bash脚本向主库不断插入数据: #!/usr/bin/env bash /opt/mysql/base/bin/mysql -uroot -p1 -S /opt/mysql/data/3306/mysqld.sock -e "create database if not exists test;use test;drop table if exists t1;create table t1(id int) " i=0 while true do /opt/mysql/base/bin/mysql -uroot -p1 -S /opt/mysql/data/3306/mysqld.sock -e "insert into test.t1 values($i)" i=$((i+1)) done 在主库的机器上,反复运行如下命令,启停semi-sync master,不超过5次必能复现这个故障。 /opt/mysql/base/bin/mysql -uroot -p1 -S /opt/mysql/data/3306/mysqld.sock -e'SET GLOBAL rpl_semi_sync_master_enabled = OFF;SET GLOBAL rpl_semi_sync_master_enabled = ON' 所以这个故障复现有了两个必要条件:- 启停semi-sync master;
- 数据库当时有事务提交。
(三)最后分析MySQL源码:
至于为什么不能每次启停semi-sync master都能触发这个故障,那我们就要看看MySQL的源码了。还好mysql-error.log明确地指出了抛出异常的位置: mysqld: /export/home/pb2/build/sb_0-19016729-1464157976.67/mysql-5.7.13/plugin/semisync/semisync_master.cc:844: int ReplSemiSyncMaster::commitTrx(const char*, my_off_t): Assertion `entry' failed. 08:02:38 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. 找到MySQL源码,抛出异常的的位置 mysql-5.7.13 semisync_master.cc。 /* wait for the position to be ACK'ed back */ assert(entry); entry->n_waiters++; wait_result= mysql_cond_timedwait(&entry->cond, &LOCK_binlog_, &abstime); 看来是这个'entry'的值为NULL,才抛出的异常,但是这还没有回答我的疑问;再看MySQL的报错信息,MySQL自己也认识到这应该是个BUG,那它有没有修复这个故障呢? 我找到MySQL最新5.7.19的版本,查看它的历史记录mysql-5.7.19 semisync_master.cc history 。 这正是我想要的修复和提交,打开看看有没有对产生故障原因的分析,Bug#22202516: ENABLING SEMI-SYNC DURING COMMIT CAN CAUSE MASTER TO ASSERT。为了方便解释这个BUG, 这里先简单的介绍一下MySQL binlog提交过程:MySQL 在5.6版本引入了Binary Log Group Commit,二进制日志的提交由此可以简化为三个阶段:
- Flush stage:将事务的日志写入binlog文件的缓存中
- Sync stage: 将binlog文件中的缓存数据写入磁盘
- Commit stage:根据顺序调用存储引擎提交事务
在BUG分析中提到:
- 首先在flush stage,由semi-sync创建一个'entry',并关联一个或一组处于flush stage的事务。这个'entry'被插入一个active transaction链表。 参考: create entry。
int ReplSemiSyncMaster::writeTranxInBinlog(const char* log_file_name, my_off_t log_file_pos)
{
...
if (is_on())
{
assert(active_tranxs_ != NULL);
if(active_tranxs_->insert_tranx_node(log_file_name, log_file_pos))
{
/*
if insert tranx_node failed, print a warning message
and turn off semi-sync
*/
sql_print_warning("Semi-sync failed to insert tranx_node for binlog file: %s, position: %lu",
log_file_name, (ulong)log_file_pos);
switch_off();
}
}
...
} - 接下来是sync stage,在sync stage每个处于这个阶段的线程将会设置trx_wait_binlog_name和trx_wait_binlog_pos为事务对应binlog的相对位置;
- 最后再commit stage,semi-sync为了等从库的确认,会用trx_wait_binlog_name和trx_wait_binlog_pos获取这个位置关联的'entry', 参考: find entry;
int ReplSemiSyncMaster::commitTrx(const char* trx_wait_binlog_name, my_off_t trx_wait_binlog_pos)
{
...
TranxNode* entry= NULL;
mysql_cond_t* thd_cond= NULL;
if (active_tranxs_ != NULL && trx_wait_binlog_name)
{
entry=
active_tranxs_->find_active_tranx_node(trx_wait_binlog_name,
trx_wait_binlog_pos);
if (entry)
thd_cond= &entry->cond;
}
...
}
四、结论:
最终修复的结果也很明显,就是遇到找不到'entry'并且从库也没有确认的情况,这个事务提交就被认为是一次异步提交。
再看一眼在哪些版本修复了这个BUG:所以升级MySQL版本可以解决问题。
五、复盘:
最后总结一下我的诊断路径:
- 观察故障出现的现象,分析MySQL的error log;
- 通过猜想和实验,构成简单的复现场景;
- 通过日志和MySQL源码的对应关系, 搜索代码历史, 定位BUG号;
- 通过阅读BUG的分析,了解异常发生的原理并知道了详细的复现条件。
事务
故障
分析
位置
情况
数据
文件
日志
源码
版本
阶段
测试
主从
函数
组件
同步
插件
重要
不断
两个
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
游戏开服务器
如何去管理前台的服务器
模拟创建软件开发团队
360对网络安全贡献伟大
江西网络营销软件开发定制排行榜
服务器远程管理叫什么
和平精英登入显示无法连接服务器
u8c连不上数据库
美国全球任意断网对网络安全
湖南ntp时钟服务器虚拟主机
服务器 管理进程
数据库商品编号商品名称商品价格
服务器数据库连不上的原因
网络技术基础全书
魔域最早服务器开服是什么时候
怎样读取维控plc的数据库
五华区第三方软件开发厂家价格
格力硬件网络技术支持
网络安全调查的图片
云服务器为什么要创建虚拟机
服务器映射打印机
株洲软件开发培训比较好
长岛电商软件开发
tp5数据库赋值给数组
防火墙强化网络安全策略
北纬科技的互联网彩票
服务器打开后不亮了怎么回复
电脑服务器管理器删了
西安展海互联网科技
git同步时出现网络安全