美文网首页
MySQL:binlog大于4G的考虑

MySQL:binlog大于4G的考虑

作者: 重庆八怪 | 来源:发表于2021-06-17 17:37 被阅读0次

    这是我的学习笔记,粗略记录备忘
    uint64:无符号8字节整数
    uint32:无符号4字节整数


    我们知道一个事务的binlog一定在一个binlog文件里面,其实一个group commit的事务都应该在一个binlog里面,那么很可能这个binlog的大小会大于4G。可以参考这篇文章:
    https://www.jianshu.com/p/10082cb72c42
    截取部分解析如下:

    事实上在第10步中我们只是设置了切换标记而已,实际的切换会等到本事务所在的commit队列都提交完成后才会进行binlog的切换,具体就是参考第28步。
    在这个期间会有2个原因导致大事务并不是binlog的最后一个事务:
    对于flush队列而言,大事务可能包含在队列中的某个位置,队列后面可能包含小事务。
    对于sync队列而言,大事务的提交会在sync阶段耗费很多时间,如果我们假设为30秒,那么在这30秒内其他新的事务是可以进入新的flush队列的,也能够进行写binlog(不是fsync)的操作。
    因此线上有压力的库,binlog的最后一个事务通常不是大事务。
    

    那么考虑这个问题,主要是因为,而且某些其他工具比如OGG等会遇到问题。

    • binlog内部的event的end log pos为4字节,如果binlog大于4G如何处理?
    • 同时主从协议中可能位点的存储为4字节,如果binlog大于4G如何处理?

    很显然uint32 最大值就是4G,那么主从该如何处理,而end log pos如何存储呢?

    一、测试图

    由于我本机已经没有那么大的空间进行测试了,因此沿用老的一张朋友的测试图如下:

    image.png

    我们这里可以看到随着binlog的增大,binlog的大小已经超过了4G,但是end log pos直接重新循环为3601大小。

    二、end log pos为什么重新循环了

    image.png

    大概这个写入路径,当我们执行提交到 flush阶段的时候会写入我们的binlog,我们稍微看看end log pos的计算方式:

    uint32 end_log_pos
          // Increase end_log_pos
          end_log_pos+= *event_len_p;
    

    这里应该是溢出了,也就是将uint64截取了剩下的4个字节直接赋予给了end_log_pos(uint32),我们直接验证一下如果,我将uint64的4294970897赋予给uint32的变量如下:

    [root@mgr4 ~]# ./a.out 
    3601
    

    我们可以发现和第一节超过4G后的end log pos一致了。这应该是end log pos重新循环的原因。但是这个值是在show slave status的时候读取和执行主库binlog position的位点,当前不知道show slave status是否会出现问题,这个也比较容易测试,做一个超大的事务大于4G就可以测试出来。当前翻阅的逻辑占时没有找到如何处理。

    三、主从协议postition为4字节的问题

    这个问题考虑一个情况,如果我们大于4G的部分包含多个事务,也就是一个group commit,如下:

    事务1、事务2、事务3位组提交,包含在一个binlog里面
    事务1 -->大于4G     
    事务2 -->从库crash
    事务3
    

    这个时候如何指向事务2和事务3的位置继续。这个时候看起来基于传统position的方式是硬伤。代码注释如下:

    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的主从).
      */
    

    也就是说我们应该使用GTID AUTOPOSITION来避免这个问题,我们可以看到确实也有这种大于4G的考虑在里面。如果是GTID字段寻址,然后将得到的位点保存在一个uint64的变量中,结合event中的event len即可继续读取。寻址的起始位点如下(dump线程读取event见下一节):

    unsigned long long
    my_off_t start_pos
    

    看来我们真的应该多使用GTID AUTOPOSITION方式了。

    四、DUMP线程读取

    前面是初始化寻址存储在unint64变量中,一旦我们寻址成功了,那么接下来就是循环读取event了。这里使用的是start_pos+event len的方式来不断的向下读取,意思就是说如果start_pos正确则可以继续读取了。大概如下(Log_event::read_log_event):

    (my_b_read(file, (uchar*) buf, LOG_EVENT_MINIMAL_HEADER_LEN)) //读取event header
    data_len= uint4korr(buf + EVENT_LEN_OFFSET);
    my_b_read(file,reinterpret_cast<uchar*>(event_data_buffer),data_len);
    

    这里我们看到读取了event的header 然后获取了data len,然后file里面包含了start_posgitd也就是通过找到的起始位点,那么就可以依次读取到event了。这里实际上也不基于end log pos,因此DUMP可以正常读取event即便大于4G。

    五、未尽事宜

    • 对于大于4G的binlog主从正常的情况下,show slave status如何显示?
    • 可以限制事务的最大binlog量通过max_binlog_cache_size,建议值4G,看来也是有这些原因在里面。
    • 其他限制?

    相关文章

      网友评论

          本文标题:MySQL:binlog大于4G的考虑

          本文链接:https://www.haomeiwen.com/subject/ztbcyltx.html