问题描述
如下图中,GTID SET已经分成了无数段。正常情况下我们的从库GTID SET应该是不会出现这种大量的空洞的,并且每次都丢失了一个GTID。如果这个时候重做从库,那么会根据GTID的下限去主库拿GTID信息,但是主库先然已经清理了这些GTID信息必然会导致报错。
问题定位与分析
其实导致出现gap与MySQL的bug有直接联系
slave-skip-errors诱因
slave_skip_errors概述
mysql在主从复制过程中,由于各种的原因,从服务器可能会遇到执行BINLOG中的SQL出错的情况,在默认情况下,服务器会停止复制进程,不再进行同步,等到用户自行来处理。
slave-skip-errors的作用就是用来定义复制过程中从服务器可以自动跳过的错误号,当复制过程中遇到定义的错误号,就可以自动跳过,直接执行后面的SQL语句。
slave_skip_errors选项有四个可用值,分别为:
off,all,ErorCode,ddl_exist_errors
默认情况下该参数值是off,我们可以列出具体的error code,也可以选择all
一些error code代表的错误如下:
1007: 数据库已存在,创建数据库失败
1008: 数据库不存在,删除数据库失败
1050: 数据表已存在,创建数据表失败
1051: 数据表不存在,删除数据表失败
1054: 字段不存在,或程序文件跟数据库有冲突
1060: 字段重复,导致无法插入
1061: 重复键名
1068: 定义了多个主键
1094: 位置线程ID
1146: 数据表缺失,请恢复数据库
1053: 复制过程中主服务器宕机
1062: 主键冲突 Duplicate entry '%s' for key %d
my.cnf中的写法
slave_skip_errors=1062,1053
slave_skip_errors=all
slave_skip_errors=ddl_exist_errors
该参数为全局静态参数,不能动态调整,可在my.cnf中加入该参数列表后重启mysql服务器生效。
必须注意的是,启动这个参数,如果处理不当,很可能造成主从数据库的数据不同步,在应用中需要根据实际情况,如果对数据完整性要求不是很严格,那么这个选项确实可以减轻维护的成本。
slave-skip-errors=all导致的bug
这个bug存在于8.0.25最新版本(包括老版本),如果我们设置了slave-skip-errors=all参数后,遇到错误后DDL和DML对于GTID的处理不一致。可能导致slave重启失败。
DDL :跳过操作,跳过GTID。GTID 不连续。
DML:跳过操作,但是不跳过GTID。GTID 连续。
数据库版本过低诱因
并没有设置skip-slave-error参数,那么是其他什么BUG导致的呢?实际上这个BUG是5.7.23以下的版本,并且设置了replicate_wild_do_table等过滤规则会后对CREATE DATABASE/ALTER DATABASE/DROP DATABASE会过滤掉操作,并且从库的GTID也会被抛弃掉,这样就产生了大量的空洞。
测试验证
选择低于5.7.23的一个版本安装主从复制
从库配置表白名单
replicate_wild_do_table=test.t1
版本5.7.20(主库)
Server version: 5.7.20-log MySQL Community Server (GPL)
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> create database test3;
Query OK, 1 row affected (0.00 sec)
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000002
Position: 1804
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 802e82b5-1efa-11ed-9196-5254000a56df:1-9
1 row in set (0.00 sec)
mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> insert into t1 values(3);
Query OK, 1 row affected (0.01 sec)
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000002
Position: 2057
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 802e82b5-1efa-11ed-9196-5254000a56df:1-10
1 row in set (0.00 sec)
从上面操作可以看到,主库进行create database的操作gtid编号是9,insert到test.t1(也就是我们配置的表白名单)gtid编号是10
版本5.7.20(从库)
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000003
Position: 600
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 802e82b5-1efa-11ed-9196-5254000a56df:1-3:7-8
1 row in set (0.00 sec)
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000003
Position: 844
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 802e82b5-1efa-11ed-9196-5254000a56df:1-3:7-8:10
1 row in set (0.00 sec)
主库信息
从库信息
结论很明显,GTID为9的事务被抛弃了,而insert t1的事务传过来了。因此出现了gtid set的gap。
下面是官网提供的bug地址
https://bugs.mysql.com/bug.php?id=88891
https://bugs.mysql.com/bug.php?id=91086
问题处理
处理也很简单,对于出现gap一般都是mysql本身的bug导致
(1)版本低于5.7.23,且需要主从配置replicate_wild_do_table,升级版本即可。
(2)如果版本高于5.7.23,则尽量避免配置slave_skip_errors=all的情况出现。
问题总结
mysql主从gtid一致,但从库出现gtid set有gap的发生,一定要先检查数据库版本,尽量不使用低于5.7.23版本数据库,对于高于5.7.23版本数据库避免配置主从复制slave-skip-errors=all。