一次MySQL主从同步异常,扒个底朝天都没排查出来……( 三 )

  • 主库 A 计算出集合 x 和集合 y 的差集,也就是集合 x 中存在,集合 y 中不存在的 GTID 集合 。比如集合 x 是 1~100,集合 y 是 1~90,那么这个差集就是 91~100 。这里会判断集合 x 是不是包含有集合 y 的所有 GTID,如果不是则说明主库 A 删除了从库 B 需要的 binlog,主库 A 直接返回错误 。
  • 主库 A 从自己的 binlog 文件里面,找到第一个不在集合 y 中的事务 GTID,也就是找到了 91 。
  • 主库 A 从 GTID = 91 的事务开始,往后读 binlog 文件,按顺序取 binlog,然后发给 B 。
  • 从库 B 的 I/O 线程读取 binlog 文件生成 relay log,SQL 线程解析 relay log,然后执行 SQL 语句 。
  • GTID 同步方案和位点同步的方案区别是:
    • 位点同步方案是通过人工在从库上指定哪个位点,主库就发哪个位点,不做日志的完整性判断 。
    • 而 GTID 方案是通过主库来自动计算位点的,不需要人工去设置位点,对运维人员友好 。
    五、恢复从库的同步
    1、查看从库执行 GTID 的进度
    在从库上执行 show slave status G 来查看 GTID 集合 。
    一次MySQL主从同步异常,扒个底朝天都没排查出来……

    文章插图
    Retrieved_Gtid_Set 表示从库收到的所有日志的 GTID 集合 。
    Executed_Gtid_Set 表示从库已经执行完成的 GTID 集合 。
    如果 Executed_Gtid_Set 集合是包含Retrieved_Gtid_Set,则表示从库接收到的日志已经同步完成 。
    这里 Executed_Gtid_Set 的集合为 1-8634831,而 Retrieved_Gtid_Set 为 1-9101426,说明从库有些 GTID 是没有执行的 。从库已经执行到了 8634831,下一个要执行的 GTID 为 8634832 。
    因为我们采用的同步方式是 GTID 方式,所以只要让从库跳过这个 GTID ,从下一个 GTID 开始同步就行 。
    带来的问题就是这个 GTID 对应的事务没有执行 。因为报错的操作是从库备份一张大表,所以从库跳过这个备份操作也是可以接受的 。
    2、手动设置 GTID
    1)重置从库进度
    首先重置下从库同步的进度 reset slave,这条命令会把所有的 relog 给清理掉,重新启用一个新的 relay log文件 。
    stop slave;
    reset slave;
    重新开启同步后,主库会计算主库 GTID 集合和从库 GTID 的集合的差集,然后主库推送差集 binlog 给从库 。
    2)设置 GTID 为一个值
    执行以下命令设置 GTID 为下一个值 。
    set gtid_next='c5d74746-d7ec-11ec-bf8f-0242ac110002:8634832';
    begin;
    commit;
    set gtid_next=automatic;
    start slave;
    gtid_next 表示设置下一个 GTID = 8634832,这个值是在原来的 8634831 加 1 。后面的 begin 和 commit 是提交了一个空事务,把这个 GTID 加到从库的 GTID 集合中 。那么从库的 GTID 集合就变成了:
    'c5d74746-d7ec-11ec-bf8f-0242ac110002:1-8634832';
    3)查看当前 GTID 集合
    我们可以通过 show master statusG 命令来查看从库的 GTID 集合 。下方截图是执行上述命令之前的 。GTID集合为 1-8634831 。另外 GTID 集合 为 1 和 GTID 集合为 1-4 的可以忽略,因为它们前面的 Master_UUID 不是当前主库的 uuid 。
    一次MySQL主从同步异常,扒个底朝天都没排查出来……

    文章插图
    show master statusG 的结果
    也可以通过 show slave statusG 命令来查看 GTID 集合,结果也是一样的 。
    3、开启从库同步
    再次启动从库的同步(start slave 命令),I/O 线程和 SQL 线程的状态都为 YES,说明启动成功了 。
    而且查看从库的同步状态时,观察到从库的同步是存在延迟的 。通过观察这个字段 Seconds_Behind_Master 在不断减小,说明主从同步的延迟越来越小了 。
    一次MySQL主从同步异常,扒个底朝天都没排查出来……

    文章插图
    两个线程都是正常运行,主从同步延迟越来越小
    过一段时间后,执行的 GTID 等于收到的 GTID 集合,Seconds_Behind_Master = 0,说明主从完全同步了 。
    六、原因
    上面的推测:备份大表造成 binlog 的一条日志太大,relay log 也跟着变大,SQL 线程无法正常解析 。
    但这是真相吗?
    虽然从库重新开启了同步,且跳过了这条日志,但带来的是从库上就不会出现这个备用表 xx_dance_0404。
    但出现了两个奇怪的问题:
    问题 1:从库开启同步后,居然出现了这个备份表 xx_dance_0404 。不是跳过这个备份操作了吗?目前没想到原因 。
    问题 2:为了重现这个问题,我到主库上做了一个备份表的操作,表名为 xx_dance_0412,从库也同步了这个新的备份表 xx_dance_0412 。而且 binlog 出现的日志现象也是一样的,对应的这条 binlog 日志也很大,但是从库同步正常 。我又备份了一张 300 万的大表,依然没重现 。


    推荐阅读