前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL疑难杂症01:主机系统表损坏导致复制全部中断

MySQL疑难杂症01:主机系统表损坏导致复制全部中断

原创
作者头像
DBA成江东
发布2024-04-13 12:08:43
930
发布2024-04-13 12:08:43
举报
文章被收录于专栏:数据库之巅数据库之巅

多年DBA工作,也遇到很多数据库疑难杂症,其处理和分析值得记录和分享,准备写一个系列文章,这是第1篇。

MySQL备机的复制全部中断是非常危险的场景,如果是io_thread异常,因为开启了半同步,直接会导致主机卡主,如果是sql_thread异常,也会导致备机延迟,主备无法自动切换,如果此时主机再故障,则业务读写都会出现异常!

1. 故障现象

收到实例A复制延迟报警,查看监控发现该实例所有备机复制都延迟了,在备机上执行

代码语言:javascript
复制
show slave status\G;

发现io_thread正常,但sql_thread异常中断,Last_SQL_Error是:

代码语言:javascript
复制
Query caused different errors on master and slave. 
Error on master: message (format)='Invalid error code' error code=126;
Error on slave:actual message='no error', error code=0. 
Default database:''. Query:'drop user if exists 'test'@'xx.xx.xx.xx'

解析binlog,发现报错事务对应的GTID是: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx: 12018988743

查看主机报错日志:

代码语言:javascript
复制
[ERROR] /usr/local/mysql/bin/mysqld: Incorrect key file for table './mysql/user.MYI'; try to repair it
[ERROR] /usr/local/mysql/bin/mysqld: Incorrect key file for table './mysql/user.MYI'; try to repair it
[ERROR] Got an error from thread_id=11, 
/export/home/pb2/build/sb_0-33648028-1555164244.06/mysql-5.7.26/storage/myisam/mi_delete.c:123

查看备机报错日志:

代码语言:javascript
复制
2024-03-28T11:42:48.384779+08:00 18280 ERROR Slave S0L for channel ': 
Worker 1 failed executing transaction
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx: 12018988743' at master log binlog.000020, end_log_pos 1031556987; 
Query caused different errors on
master and slave. Error on master:
message (format)='Invalid error code' error code=126; 
Error on slave:actual message='no error',
error code=0. Default database:'
Query:'drop user if exists 'test'@'xx.xx.xx.xx'

查看主机binlog日志:?

查看备机binlog日志:?

2. 修复过程

一般遇到复制报错,在确保数据一致性的情况下,有2种方案:跳过复制出错事务,或者不记binlog手工执行语句。

2.1 尝试跳过报错事务

代码语言:javascript
复制
STOP SLAVE;
SET GTID_NEXT='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:12018988743';
BEGIN; COMMIT;
SET GTID_NEXT='AUTOMATIC';
START SLAVE;

发现没有效果

2.2 尝试人工补全报错事务

代码语言:javascript
复制
stop slave;
SET session sql_log_bin = 0;
drop user if exists 'test'@'xx.xx.xx.xx';
start slave;

发现没有效果

2.3 重置备机复制状态

这时深入分析备机复制状态,发现备机的Executed Gtid Set: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:1-12018988746。?12018988746大于12018988743,表明报错的事务已经执行过并执行成功了!

那么我们是否可以重置备机复制状态,然后用GTID自动找点恢复复制?

代码语言:javascript
复制
stop slave;reset slave all;
change master to master_host='xx.xx.xx.xx', master_port=xxxx, master_user='xxxx', 
master_password='xxxx', master_auto_position=1;start slave;

复制恢复,尝试成功!

说明: 在 MySQL 中,RESET SLAVE [ALL]?命令用于重置复制从服务器的状态。这个命令会清除从服务器上与复制相关的所有状态和配置,使其停止从主服务器接收复制事件,并准备重新配置复制。

  • RESET SLAVE:不带?ALL?关键字时,该命令会删除从服务器上的中继日志(relay logs),并重置复制相关的系统变量,但不会改变复制配置(如主服务器的地址和复制的用户凭证)。
  • RESET SLAVE ALL(在 MySQL 5.5.16 及更新版本中可用):当使用?ALL?关键字时,该命令不仅执行?RESET SLAVE?命令的所有操作,还会清除复制配置,包括主服务器的信息、用户凭证等。这意味着,执行?RESET SLAVE ALL?后,你需要重新配置复制关系,包括执行?CHANGE MASTER TO?语句来指定新的主服务器信息。

注意虽然重置了复制状态,但是已经执行的事务的GTID信息不变,所以可以用自动找点恢复复制关系!

3. 原因分析

修复问题后,我们来深入分析下原因:

3.1 主机mysql.user表被破坏

MySQL5.7中mysql.user为MyISAM引擎,实例异常crash后,触发表损坏。

3.2 下游复制因为err_code不一致中断

MySQL5.7 binlog格式设置为row模式,但DCL语句(Data Control Language 语句是指用于控制数据库系统中数据访问和权限的SQL语句)会记录query event,下游备机回放query event,会检查relay日志和下游本地回放的err_code信息,如果不一致,复制会报错。我们来看下具体代码:

代码语言:javascript
复制
if((expected_error&& rpl_filter->db_ok(thd->db().str) &&
 expected_error != actual_error &&
 !concurrency_error_code(expected_error)) &&
 !ignored_error_code(actual_error) &&
 !ignored_error_code(expected_error))
 {
 if (!ignored_error_code(ER_INCONSISTENT_ERROR))
 {
 rli->report(ERROR_LEVEL, ER_INCONSISTENT_ERROR,
 ER(ER_INCONSISTENT_ERROR),
 ER_THD(thd, expected_error), expected_error,
 (actual_error ?
 thd->get_stmt_da()->message_text() :
 "no error"),
 actual_error, print_slave_db_safe(db), query_arg);
 thd->is_slave_error= 1;
 }

代码说明:

  1. 错误检查条件
    • expected_error && rpl_filter->db_ok(thd->db().str):预期错误(expected_error)不为零,且当前数据库(通过线程的 db 属性获取)通过了复制过滤(rpl_filter->db_ok)检查。
    • expected_error != actual_error:预期的错误代码与实际发生的错误代码不相同。
    • !concurrency_error_code(expected_error):预期的错误不是并发错误。
    • !ignored_error_code(actual_error) && !ignored_error_code(expected_error):既实际发生的错误也预期的错误都不是被忽略的错误。 如果以上所有条件都满足,进入错误处理逻辑。
  2. 错误处理
    • 首先,检查?ER_INCONSISTENT_ERROR(一个特定的错误码,指示数据或状态不一致)是否不被忽略。如果不被忽略,那么:- 使用?rli->report?方法报告一个?ER_INCONSISTENT_ERROR?错误,这表明有一个不一致的错误状态发生。这里会包含一些错误信息,如预期的错误码、实际的错误信息(如果有的话)、实际的错误码、受影响的数据库名(经过安全处理),以及相关的 SQL 查询(query_arg)。
    • 将线程标记为遇到了从服务器错误(thd->is_slave_error= 1;)。

3.总结:

这段代码的主要作用是在复制过程中检测错误状态的一致性。当预期发生某个错误,但实际发生的错误与之不同时(并且这些错误都不是并发错误或被忽略的错误),代码将报告一个不一致的错误(ER_INCONSISTENT_ERROR),并将当前线程标记为遇到了错误。这个机制是数据复制中错误处理和数据一致性维护的重要部分,确保了在复制过程中遇到异常情况时,能够及时识别并报告问题。

因为主机err_code是126(Index file is crashed),而备机执行成功,error code 是0,2者不一致,所以复制报错中断!

现在还有1个问题,为什么sql_thread没有停在GTID 12018988743,而是又往前执行了3个事务,GTID停留在 12018988746 ?分析线上实例都开启了并行复制,所以怀疑和并行回放有关,我们找到报错事务的last_commited=163886,然后grep下,发现确实事务12018988743-12018988746对应的last_commited都是163886。

说明: last_committed表示事务在每个二进制日志文件中的 Binlog Group 编号,sequence number 为每个二进制日志文件中事务的编号,last committec会有重复的值,last_committed 值相同表示事务处于同一个 Binlog Group 中、也表示主库中这些事务在并行提交时没有锁冲突。从库在应用二进制日志时,具有相同ast_committed值的事务可以并行回放,在每个二进制日志文件中,sequence_number的值不允许重复。

4. 规避方法

升级到MySQL8.0,系统表全部换成事务型的innodb表。若系统表写入失败,不会记录binlog,也就不会导致上面的复制中断问题。

微信公众号"数据库之巅"记录了我在互联网金融数据库运维中走过的路和踩过的坑,感兴趣的同学可以关注。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 故障现象
  • 2. 修复过程
    • 2.1 尝试跳过报错事务
      • 2.2 尝试人工补全报错事务
        • 2.3 重置备机复制状态
        • 3. 原因分析
          • 3.1 主机mysql.user表被破坏
            • 3.2 下游复制因为err_code不一致中断
              • 4. 规避方法
              相关产品与服务
              云数据库 MySQL
              腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
              http://www.vxiaotou.com