暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

线上环境MySQL冷备恢复导致复制启动失败一例

GrowthDBA 2021-11-24
1198

先来介绍一下问题背景:

同事A申请了一台服务器S自行安装(非交维:没有移交到运维部门统一规范管理)MySQL 5.7.13当作测试环境,因申请时,只考虑作为测试环境使用,所以MySQL没有开启binlog,也没有备份

阴差阳错地,服务器S交到了另一个同事B手上,此时,机器的定位尚不明确,但是同事B在正式使用服务器S之前让我协助帮他们每天做备份,备份也没有强一致的要求。随着开发的迭代,这个库慢慢成为了生产环境。但还是没有正式交维(我们的工作流:如需交维,是需要向我们运维部门提交工单的)、机器的OWNER没有发生变更还是同事A(没有交维的机器,我们不会接手进行规范管理的,懂得都懂~)。

就在昨晚,同事A提交了机器回收申请(回收逻辑是服务器OWNER两次确认手动输入'确认回收指定主机xx.xx.xx.xx'文字后,运管平台直接会将对应服务器铲除)。服务器S属于同事A,在回收的时候没有和同事B沟通确认,直接回收导致了这次事故。然后同事B准备登录时发现连接不上服务器S了,经过运维部门SA同事协助排查,发现机器已经被推掉。

PS:后来了解到,这个库虽然数据量不大,但数据非常重要。存放的是用于参加ISO-27001信息安全体系认证评审的数据。

坑就是这样这样来的,没有开启binlog的MySQL服务器直接被推掉了,这种事情比删库还严重,唯一庆幸的是:每天必做的冷备份,并将备份文件传输到中间备份机。还有一点,文章素材有了

原环境情况介绍




因没有交维、MySQL没有开启binlog、同事B也没有备份强一致性的要求且数据量不大,我使用了mysqldump工具进行备份:
/usr/local/mysql/bin/mysqldump -u root -pxxxxxx -A --default-character-set=utf8 --hex-blob > /tmp/mysql_fullbackup_`date +%Y-%m-%d_%H-%M-%S`.sql
小提示

需要注意几点:

1、-p选项后面跟的是用户密码,-p选项和密码之前是不能有空白字符' '的,会发生不识别的情况,其他选项如-u、-P、-h可以跟空白字符。

2、mysqldump工具的--hex-blob选项,是将BINARY、VARBINARY、BLOB、BIT等二进制类型数据导出为十六进制,否则导出的文件会出现“乱码”的情况,同时,这个“乱码”在传输过程中,很容易发生改变。

3、mysqldump进行一致性备份时必须使用的两个参数:--single-transaction、--master-data=2。

--single-transactionhttps://dev.mysql.com/doc/refman/5.7/en/mysqldump.html#option_mysqldump_single-transaction(官档描述)

①此选项会将隔离级别设置为:REPEATABLE READ。在一个事务里对全部表获取一个一致性快照,让整个数据在dump过程中保证数据的一致性;
②这个选项只对支持事务的存储引擎InnoDB表有用,且不会锁表。但是这个不能保证MyISAM表和MEMORY表的数据一致性;
③在添加--single-transaction选项dump这个过程中,因为一致性读不能隔离ALTER TABLE, CREATE TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE上述语句。所以如果在dump过程中,使用上述语句,可能会导致dump出来的文件数据不一致或者不可用。
④自动关闭--lock-tables选项。

--master-datahttps://dev.mysql.com/doc/refman/5.7/en/mysqldump.html#option_mysqldump_master-data(官档描述)

--master-data选项主要是为了记录binlog的filename和position,用于之后基于时间点的恢复,--master-data=2就是将CHANGE MASTER TO ...语句注释,以便我们恢复时可以再次确认,--master-data=1为不注释CHANGE MASTER TO ...语句。--master-data选项会自动关闭--lock-tables。它也会打开--lock-all-tables,除非--single-transaction选项也被指定,在这种情况下,仅在dump开始时短时间内获取全局读锁。
综上所述,--single-transaction和--master-data=2一起使用时,可以保证备份的一致性和更短的加锁时间。(mysqldump工具后面会单独开辟文章进行详解)

本例中对备份无一致性要求且没有开启binlog,所以我们没有使用上述两个参数,直接进行了全实例的冷备份。




数据恢复 & 问题描述、定位、处理



数据恢复

发现机器被回收后,项目组同事立即申请两台主机,正式将数据库交维,我们按照一主一从的架构进行恢复搭建。新服务器MySQL数据库介绍:
MySQL Version:5.7.34(2台)MySQL Port:3306数据同步方式:ROW+GTID+增强半同步

环境搭建的流程如下:

1、主库环境安装MySQL,开启binlog、GTID;

2、将备份文件导入新库,新库生成对应的GTID SET;

mysql3306 -p < mysql_fullbackup_`date +%Y-%m-%d_%H-%M-%S`.sqlshow master status\G;

3、从库环境安装MySQL,开启binlog、GTID,创建同步账户并设置复制;
CREATE USER 'repl'@'xx.xx.xx.%' IDENTIFIED BY 'xxxxxx';GRANT REPLICATION CLIENT,REPLICATION SLAVE on *.* to 'repl'@'xx.xx.xx.%';reset master;reset slave all;stop slave;CHANGE MASTER TO MASTER_HOST='xx.xx.xx.xx',MASTER_USER='repl',MASTER_PASSWORD='xxxxxx',MASTER_PORT=3306,master_auto_position=1;start slave;

4、在从库回放的过程中,我在从库设置并执行了备份操作,备份脚本核心备份语句如下:

xtrabackup --defaults-file=${mysqlconf} --lock-wait-timeout=300 --slave-info --safe-slave-backup --compress --parallel=${parallel} --user=${mysqluser} $([[ "${mysqlpassword}" = "" || ! "${mysqlpassword}" ]] || echo " --password=${mysqlpassword}") --tmpdir=${mysqlbackupdir} --stream=xbstream ${mysqlbackupdir} 2>>$backup_log | ssh -i ${scriptpath}/id_rsa backup@${backuphost} "xbstream -x -C ${backupdir}/${localhost}/mysql${mysqlport}/${time}" >>$backup_log 2>&1

问题描述

备份使用的是percona-xtrabackup 2.4版本,设置了--compress压缩、--stream=xbstream流式备份,直接将备份文件传输到备份机上,因为备份操作是在从库上,并且添加了--safe-slave-backup参数选项,问题就发生在这个参数上。
先来看下PERCONA官网对这个参数选项的描述:https://www.percona.com/doc/percona-xtrabackup/2.4/innobackupex/innobackupex_option_reference.html#cmdoption-innobackupex-safe-slave-backup

这段描述的含义是:当指定了这个参数选项,备份将在运行FLUSH TABLES WITH READ LOCK之前停止slave SQL thread并等待开始备份,直到SHOW STATUS中的Slave_open_temp_tables为零。如果没有打开的临时表,则进行备份,否则将启动和停止SQL thread,直到没有打开的临时表。如果在指定参数选项--safe-slave-backup-timeout多少秒后Slave_open_temp_tables没有变为零,则备份将失败。备份完成后,slave SQL thread将重新启动。
本例中,没有备份成功,备份日志报错的大概内容就是SQL thread没有启动起来(抱歉,昨天处理问题时忘记截图了),Slave_IO_Running: YesSlave_SQL_Running: No
当时看到报错,提示查看ERROR Log:
more /mysql/mysql3306/bj-xx-xx-mysql-prod-xxxx.xxxxxxxxx.com.err | grep -C 20 'ERROR'

看如图报错,第一个红框是备份工具在备份过程中触发的:
stop slave sql_thread;触发提示:[Note] Error reading relay log event for channel '': slave SQL thread was killedstart slave sql_thread;触发报错:[ERROR] Error reading slave worker configuration[ERROR] Error creating relay log info: Failed to initialize the worker info structure.

于是乎,我在从库手动执行启动复制的语句:

mysql> start slave;ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repository

手动执行的时候也报错了,错误日志见上述报错截图的第二个红框。然后我又查询了一下表mysql.slave_master_info、mysql.slave_relay_log_info数据:

SELECT * FROM mysql.slave_master_info;SELECT * FROM mysql.slave_relay_log_info;
结果为空Empty Set。

问题定位

根据手动执行启动复制命令得到错误信息:启动复制时,使用repository中信息初始化relay log结构失败了。错误日志中也提示创建relay log报错:无法初始化worker进程

根据错误日志中的信息去主库排查一下对应binlog的日志(未发现问题):

/usr/local/mysql3306/bin/mysqlbinlog -v -v --base64-output=decode-rows --start-position=5442597 /mysql/mysql3306/mysql-bin.000003 | more

问题处理

其实问题到这里就很奇怪,没有详细的报错信息,所以就采用了一个'死马当活马医'的办法。前提是确保数据是准确无误的,因为还在恢复过程中,未交付给研发团队,程序还并没有连接数据库进行写入操作,同时我们做了什么操作都清楚,而且冷备份的文件一直都在,即使出了问题,我们也可以再手动恢复。
那么,根据报错的提示,初始化relay log失败了,且我们已经确认了mysql.slave_master_info、mysql.slave_relay_log_info表为空。mysql.slave_master_info、mysql.slave_relay_log_info表中保留了以前的复制信息,但是现在表数据为空,导致从库启动复制时无法找到对应要Replay的Rely Log文件。所以需要清空mysql.slave_master_info、mysql.slave_relay_log_info表和删除所有所有Relay Log,并重新指定同步信息创建出新的Relay Log,说白了就是重新告诉SQL thread从哪里开始同步
reset slave;CHANGE MASTER TO MASTER_HOST='xx.xx.xx.xx',MASTER_USER='repl',MASTER_PASSWORD='xxxxxx',MASTER_PORT=3306,master_auto_position=1;start slave;

最后校验一下同步状态:

show slave status\G;

同步状态正常,至此,问题解决,恢复工作全部完成。哦,对了不要忘记配置备份、监控和报警,合格的DBA怎么能忘记这些呢,既然交维了,就要显出我们的专业来。备份的要求是每日凌晨4点,所以我们设置的是凌晨4点的定时任务。第二天,我们验证一下备份的情况:
crontab -l | tail -n 1cat backup3306.log | tail -n 3cat backup_result.log

完美!
小提示
reset slave;命令触发的操作:
1、清空mysql.slave_master_info、mysql.slave_relay_log_info两张表的数据;
2、删除所有Relay Log文件,并重新创建新的Relay Log文件;(我记得清空后重新创建的Relay Log文件的起始位置信息是:Relay_Log_File: relay-bin.000001、Relay_Log_Pos: 4,经验之谈)
3、不会改变gtid_executed或者gtid_purged的值,如果使用reset slave all;命令会清空两个值,那样的话,从库Replay过的Event会重新从主库拉取,重复Replay一遍,那样的话,同步1个Event,就会报冲突的错误(因为从库已经有数据了,再同步一遍,肯定会发生一致性冲突),如果像本例中3000+多个,可想而知。
注意:不要手动调整mysql.slave_master_info、mysql.slave_relay_log_info两个表的数据,以免出现问题



小结




本文是一例不很常见的恢复复制相关问题,希望此文当大家遇到类似问题时,可以帮助到大家,通过这个线上问题,更加知道了备份的重要性,别到时候真发生删库情况时,只能跑路。今天文中提到很多关于复制相关的知识点,后期也会慢慢更新有关复制原理的文章,大家拭目以待。又是干货满满的一天,我们下篇文章见,加油!~



end


文章转载自 GrowthDBA,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论