MySQL:主从binlog超过4G导致报错

一、问题描述

最近遇到的问题,问题是在5.7上出现的,这个问题大概会有如下主要的报错,报错后主从中断

2024-04-23T09:54:45.586576+08:00 23 [ERROR] Slave I/O for channel '': Unexpected master's heartbeat data: inconsistent heartbeat event content; the event's data: log_file_name log_bin.000001k
log_pos 1(代表本次接收到心跳的位点), Error_code: 1623
2024-04-23T09:54:45.586608+08:00 23 [ERROR] Slave I/O for channel '': Relay log write failure: could not queue event from master, Error_code: 1595
2024-04-23T09:54:45.586612+08:00 23 [Note] Slave I/O thread exiting for channel '', read up to log 'log_bin.000001', position 154(代表io线程当前读取到的位置)
2024-04-23T11:10:29.466440+08:00 29 [ERROR] Slave I/O for channel '': Unexpected master's heartbeat data: heartbeat is not compatible with local info; the event's data: log_file_name log_bin.000001K4g_pos 21(代表本次接收到心跳的位点), Error_code: 1623
2024-04-23T11:10:34.388659+08:00 29 [ERROR] Slave I/O for channel '': Relay log write failure: could not queue event from master, Error_code: 1595
2024-04-23T11:10:34.388703+08:00 29 [Note] Slave I/O thread exiting for channel '', read up to log 'log_bin.000001', position 154(代表io线程当前读取到的位置)

其中它们的区别在于错误的第一句,但是其实本质上没有什么区别

  • inconsistent heartbeat event content,代表本次接收到心跳的位点必须大于4
  • heartbeat is not compatible with local info,代表io线程当前读取到的位置必须大于本次接收到心跳的位点

注意这里是模拟的

Slave I/O thread exiting for channel '', read up to log 'log_bin.000001', position 154

线上环境如果报错的话,报错中的postition一般会比较大,比如大于4G。报错后主从的状态显示如下,

                Last_IO_Error: Relay log write failure: could not queue event from master
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 333900
                  Master_UUID: e8eb8ce3-2ccb-11eb-832e-000c29621fc8
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 240423 11:10:34

二、问题分析

这个问题根据BUG的描述(BUG随后贴出),主要涉及到主从之间心跳event的发送,心跳发送时间和slave_net_timeout参数有关,默认slave_net_timeout为60秒,而所有主从通道心跳发送时间为30秒,也就是slave_net_timeout的一半,这样能够保证在主从超时之前心跳能够检测到,曾今有一个案例是建立主从通道后改小了slave_net_timeout参数,导致主从不断地重连,生成了大量relay log,如下,

具体的心跳时长可以查看channel的配置,如下

select CHANNEL_NAME,HEARTBEAT_INTERVAL from performance_schema.replication_connection_configuration;

心跳event是不会记录到relay log的,也就是io线程会将其处理,当处理的时候会通过发送过来的数据进行初始化和判断,如下

  • 初始化的时候会判断发送的位点是否大于BIN_LOG_HEADER_SIZE(4字节,binlog文件开头的4个字节),否则报错就是 inconsistent heartbeat event content
  if ((log_ident != NULL && header()->log_pos >= BIN_LOG_HEADER_SIZE)) //这里判定
    is_valid_param= true;
  • 判断的时候会判定发送的位点和当前io线程接收到的位点,如果发送的心跳位点比当前接收的位点还要小就会报错,报错就是heartbeat is not compatible with local info
    if (memcmp(const_cast<char *>(mi->get_master_log_name()),
               hb.get_log_ident(), hb.get_ident_len())
        || (mi->get_master_log_pos() > hb.common_header->log_pos)) //这里判定
    {
      /* missed events of heartbeat from the past */
      char errbuf[1024];
      char llbuf[22];
      sprintf(errbuf, "heartbeat is not compatible with local info; "
              "the event's data: log_file_name %-.512s log_pos %s",
              hb.get_log_ident(), llstr(hb.common_header->log_pos, llbuf));
      mi->report(ERROR_LEVEL, ER_SLAVE_HEARTBEAT_FAILURE,
                 ER(ER_SLAVE_HEARTBEAT_FAILURE), errbuf);
      goto err;
    }

那么问题就是什么时候dump线程在发送心跳event的时候位点出现错乱,这个问题实际上比较简单,就是当binlog文件大于4G的时候,因为发送的位点是一个uint32类型的,这里大于4G会出现截断,在Binlog_sender::send_heartbeat_event中我们很容易看到如下,

  int4store(header + SERVER_ID_OFFSET, server_id);
  int4store(header + EVENT_LEN_OFFSET, event_len);
  int4store(header + LOG_POS_OFFSET, static_cast<uint32>(log_pos));
  int2store(header + FLAGS_OFFSET, 0);

这里log_pos是一个unsigned long long类型的,而存储的时候转换为了unsigned int,肯定在大于4G的会出现截断,一旦截断后就会导致这种问题。

最后涉及到一个最重要的问题,什么时候发送心跳 event?

  • 心跳event一般是在没有正常的event发送的时候才会去发送心跳event,也就是说当dump线程读取到当前binlog的末尾才会去发送心跳event,发送频率就是前面说的心跳时长。
  • 启动主从时候,如果是包含了GTID,则会根据从库的GTID来判断哪些binlog event需要发送给从库,这个时候有一个过滤的过程,这个期间也需要不断地发送心跳event给从库来更新master postition位置,发送也取决于心跳时长。

但是我这里明显是正常的主从并没有重启过,那么考虑主要是第一点,主库切换binlog时间可能较长,因此遇到这个问题的机率并不会太大。

三、相关BUG和测试

这个问题已经是一个比较老的BUG了,但是一直没有修复,可能是考虑到兼容性的问题吧,如下,

测试比较简单,如果要模拟大事务确实比较困难,但是我们直接修改一下pos的位置,因此2的32次方最大为4294967296,因此我们修改为4294967297,就可以出现第一个错误,如果修改为4294967317就可以出现第二种错误。如下,


image.png

四、8036测试

接下来就是要测试一下MySQL的新版本是否也会如此,在测试中视乎有些许不同,最后在show slave status居然卡主了,但是没有做仔细分析了,但是报错依旧是如下,

2024-04-23T12:02:49.615140+08:00 10 [ERROR] [MY-013118] [Repl] Replica I/O for channel '': Unexpected source's heartbeat data: Replication heartbeat event contained the position 317 which is smaller than the position 4294967317 that was computed from earlier events received in the stream. The filename is 'mysql-bin.000035'. Error_code: MY-013118
2024-04-23T12:02:49.615310+08:00 10 [ERROR] [MY-013122] [Repl] Replica I/O for channel '': Relay log write failure: could not queue event from source, Error_code: MY-013122
2024-04-23T12:02:49.615352+08:00 10 [Note] [MY-010570] [Repl] Replica I/O thread exiting for channel '', read up to log 'mysql-bin.000035', position 4294967317

看来这个问题到最新版本依旧存在,但是发送心跳event的时机不知道有没有变化。

五、如果避免

  • 避免大事务,大事务做拆分,不要让binlog大于4G,这是最好的办法
  • 如果确实有大事务,并且经常导致binlog大于4G,并且经常遇到这个错误,可以考虑调大slave_net_timeout参数,重做主从。
  • 报错后,如果是postition主从报错后重启可能都不行,但是GTID得主从应该可以重启解决,postition的主从无法发送大于4G以上的binlog位点,如下,
com_binlog_dump(传统基于位点的主从)
/*
    4 bytes is too little, but changing the protocol would break
    compatibility.  This has been fixed in the new protocol. @see
    com_binlog_dump_gtid(基于GTID AUTOPOSITION的主从).
  */
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容