背景
Mysql主从基于gtid复制被广泛使用,DBA可能遇到从库上的复制故障及处理方法整理
本系列按照做项目的处理方式,每篇包含两个故障问题
写在前面
参考知数堂师兄徐晨亮的微信公众号,链接如下(20201111测试链接可访问),推荐关注他噢( mysql code tracer)。
01
Last_IO_Errno: 1236
#Last_IO_Error:
Got fatal error 1236 from master when reading data from binary log:
'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1,
but the master has purged binary logs containing GTIDs that the slave requires.
#疑问(请根据图片中语句的执行顺序和报错信息阅读以下分析信息)
为什么master上的GTID大于slave上的GTID但是还是会提示找不到binlog呢?
原因
从库异常停止了io_thread,之后主库更新了gtid_purged值(清理了binlog文件),最后导致重新开启io_thread时,报错。PURGE BINARY LOGS TO 'mysql-bin.000007'; Query OK
原来slave去master上拉取binlog时,会判断Executed_Gtid_Set值与主库上的gtid_purged值,
而在t4时刻执行了purge binary logs,那么即会更新master上的gtid_purged值。
此时slave上去拉取binlog时,发现本地Executed_Gtid_Set要小于master上的gtid_purged值。
我使用的方法
#如果涉及的库表数据量不大并且主库上的binlog包含所需的数据(重新从主库拉取binlog)
1.先备份整个库
mysqldump --single-transaction --master-data=2 -R -E --triggers --all-databases > test.sql
2.删除库
3.重新设置主从
reset slave all;
reset master;
change master to master_host='192.168.66.133',master_user='msandbox',master_port=17261,
master_password='123456',master_auto_position=1;
原作者的解决方案
方法一
更改复制为位置点复制,建立连接后再次改为基于GTID的复制方式(个人理解:跳过错误的事务)
(跳过事务是要保证跳过的是无用的也就是跳过之后涉及的库表在主从上都是一致的)
show slave status\G
#记录
Master_Log_File: mysql-bin.000008[保证需要数据在这个文件中,主库没有flush logs]
Exec_Master_Log_Pos: 349595 [看看执行到什么位置了pos]
stop slave;
master_host='192.168.66.133',master_user='msandbox',master_port=17261,
master_password='123456',master_log_file='mysql-bin.000008',master_log_pos=349595,
master_auto_position=0;
[使用哪个二进制文件和位置,要在主库上查看使用show binary logs;和mysqlbinlog,需要自己确定哟,
不一定是上面记录中的值]
CHANGE MASTER TO MASTER_AUTO_POSITION=1;
start slave;
#在主库解析binlog,得到事务的gtid
mysqlbinlog mysql-bin.000008 --database test --base64-output=decode-rows -vv
--skip-gtids=true |grep -C 1 -i "at 349595" > 11.log
理论上不推荐跳过事务这种操作,除非跳过的是一个空事务。(熟练了再使用,不熟练就重建主从关系)
02
参考博文在以下链接 https://www.fordba.com/mysql-gtid-maintain-two-case.html
(原博客中事务执行流程图)
报错信息
Relay_Log_File: mysql-relay.000011(之后要使用mysqlbinlog解析)
Relay_Log_Pos: 929 (从库上找语句)
#主库查看事务
select @@GLOBAL.gtid_executed;
从库让 SQL THREAD 跳过一个GTID事务的时候,想让从库重新执行这个跳过的GTID,却始终报错。
原因
也就是我们在从库 手动跳过了 删除表的事务,之后又创建同名表
原作者的解决方案
STOP SLAVE;
CHANGE MASTER TO RELAY_LOG_FILE='mysql-relay.000011' ,RELAY_LOG_POS=749
,MASTER_AUTO_POSITION=0;CHANGE MASTER TO MASTER_AUTO_POSITION=1;
START SLAVE;
SHOW SLAVE STATUS\G
原作者的第二个案例,涉及的知识点如下
relay_log_recover=0
slave会扫描最后一个relay log文件,Retrieved_Gtid_Set显示的是当前扫描所得的GTID;
io线程会通过扫描所得的最后一个GTID+1(如果Retrieved_Gtid_Set>=Executed_Gtid_Set)为依据来拉取。
relay_log_recover=1
slave中把所有relay log清除,io线程通过Executed_Gtid_Set后的+1个事务
开始拉取并生成新的relay log文件;SQL线程在清除relay log时把Relay_Log_File、
Relay_Log_Pos设为空,所以SQL线程从新的relay log文件的第一个事务开始应用
从库:
RESET MASTER; #清空从库的gtid_executed和binlog信息(清空并重建从库上的mysql-bin.000001)
SET GLOBAL GTID_PURGED='1111:1-850'
#手动让从库认为1111:845已经执行
flush logs;(新增binlog文件和relaylog文件)
-- reset slave;
#(之后需要重新change master to)清空从库的gtid_Retrieved和relaylog信息
(清空并重建mysql-relay.000001)
从服务器连接到主服务器之后,把自己执行过的GTID(Executed_Gtid_Set)<SQL线程> 、
获取到的GTID(Retrieved_Gtid_Set)<IO线程>发给主服务器,
主服务器把从服务器缺少的GTID及对应的transactions发过去补全即可。
取自以下
原博主写的太好了,作为学习者,以下内容直接引用了。
link
gtid_executed(global):MySQL数据库已经执行过的Gtid事务,处于内存中。
show master status/show slave status中的Executed_Gtid_Set也取自这里。
gtid_purged(global):由于binlog文件的删除(如purge binary logs或者超过expire_logs_days设置)
已经丢失的Gtid事务,它是gtid_executed的子集
参数 binlog_gtid_simple_recovery
数据库服务启动时,gtid_executed和gtid_purged按下面方式初始化
binlog_gtid_simple_recovery=FALSE
• gtid_executed:从mysql-bin.index的末行往首行所对应的binlog查找,
直到首个被找到包含Previous_gtids_log_event的binlog。
然后读取这个binlog的Previous_gtids_log_event和Gtid_log_events中的所有Gtid集合保存到内部变量
gtids_in_binlog。然后使用gtids_in_binlog和mysql.gtid_executed表的并集初始化gtid_executed变量
如果你有大量非GTID的binlog(比如gtid_mode=off的情况下创建),
初始化gtid_executed的过程会消耗较长的时间。
• gtid_purged:从mysql-bin.index的首行往末行所对应的binlog查找,
直到首个被找到包含非空Previous_gtids_log_event或者Gtid_log_event的binlog。
然后读取这个binlog的Previous_gtids_log_event,
将gtids_in_binlog - Previous_gtids_log_event得到的集合保存到内部变量
gtids_in_binlog_not_purged。
最后使用gtid_executed - gtids_in_binlog_not_purged初始化gtid_purged变量
#默认值
binlog_gtid_simple_recovery=TRUE(MySQL5.7.7及以上默认)
只迭代mysql-bin.index的首行和末行所对应的binlog,
gtid_executed和gtid_purged的值就是取这两个binlog中的
Previous_gtids_log_event/Gtid_log_event计算,
当然gtid_executed变量的值还要结合mysql.gtid_executed
参数 mysql.gtid_executed
如果没有开启log_bin或者没有开启log_slave_updates,从库在应用relay-log中的
每个事务会执行一个insert mysql.gtid_executed操作。这只针对从库而言~
#一般情况
如果开启log_bin,在binlog发生rotate(flush binary logs/达到max_binlog_size)或者关闭服务时,
会把所有写入到binlog中的Gtid信息写入到mysql.gtid_executed表。这适用于主库和从库~
log_bin=on,MySQL 8.0.17 起每个事务提交时会更新mysql.gtid_executed表
例子:从库log_bin=on,log_slave_updates=off,那么在应用relay-log时会实时写入
mysql.gtid_executed,而在从库直接写入数据,需要等到发生rotate或者关闭服务才写入~
mysql.gtid_executed压缩:log-bin=off,每gtid_executed_compression_period压缩一次;
log-bin=on,日志切换时压缩~
如果发生异常crash,当前binlog中的Gtids信息没能写入到mysql.gtid_executed表,
在恢复过程通过读取binlog中的Previous_gtids_log_event/Gtid_log_event信息
把这些Gtids添加到mysql.gtid_executed表和gtid_executed系统变量
#Rotate(主从切换)
# 解析mysql-bin.000208
mysqlbinlog -vv --base64-output=decode-rows mysql-bin.000208 |more
本文说明,主要技术内容来自互联网技术大佬的分享,还有一些自我的加工(仅仅起到注释说明的作用)。如有相关疑问,请留言,将确认之后,执行侵权必删