暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【华为云MySQL技术专栏】Binlog压缩:节省存储,优化网络,提升性能

GaussDB数据库 2025-04-25
146

1


背景及概述


Binlog(二进制日志)记录了数据的更改操作,具有极为重要的作用,可用于数据恢复、数据复制和审计。然而,当用户在使用MySQL时,如果写入业务量过大,可能会导致磁盘空间和网络带宽的过度使用。

为了应对这一情况,MySQL 8.0.20版本推出了基于zstd压缩算法的binlog压缩功能。


本文将从源码角度对binlog压缩功能进行浅析,帮助读者了解其使用方式。同时,通过测试结果展示该功能的效果和性能影响体现binlog压缩功能为用户带来了较好的应用价值。


2


原理浅析


MySQL 8.0.20版本增加的binlog日志事务压缩功能,通过使用zstd算法对事务信息进行压缩,然后再写入binlog文件。压缩事务由Transaction_payload_log_event对象处理,该对象继承自Transaction_payload_eventLog_event。压缩后的事务内容保存在Transaction_payload_event中,核心成员如下所示。


    class Transaction_payload_event : public Binary_log_event {
      protected:
      //event所包含的数据信息
      const char *m_payload{nullptr};


      //压缩后的数据信息大小
      uint64_t m_payload_size{0};


      //压缩方式,定义见后,当前仅包含代表非压缩的NONE和ZSTD方式
      transaction::compression::type m_compression_type{
          transaction::compression::type::NONE};


      //压缩前的数据信息大小
      uint64_t m_uncompressed_size{0};
      ......
    }


    //定义压缩方式
    enum type {
      /* ZSTD compression. */
      ZSTD = 0,


      /* No compression. */
      NONE = 255,
    };

    压缩binlog的时机为写入缓存并提交事务时,通过调用this->compress(thd)来完成压缩动作,相关调用堆栈及函数如下所示。


      调用堆栈:
      #0  in binlog_cache_data::finalize
      #1  in MYSQL_BIN_LOG::commit
      #2  in ha_commit_trans
      #3  in trans_commit
      #4  in mysql_execute_command
      #5  in dispatch_sql_command
      #6  in dispatch_command
      #7  in do_command


      int binlog_cache_data::finalize(THD *thd, Log_event *end_event) {
          ……
          if (int error = flush_pending_event(thd)) return error;
          if (int error = write_event(end_event)) return error;
          if (int error = this->compress(thd)) return error;
          ……
      }

      binlog压缩处理函数即为binlog_cache_data::compress,其流程图如下

      图1 binlog压缩处理流程图


      该函数首先会判断binlog_transaction_compression参数对应的变量,如果为true,则继续进行压缩处理。接着,函数会判断是否存在Incident events、非事务性更改等非压缩场景,若不存在,则进行压缩。通过这些场景校验后,函数获取压缩器,并根据需要申请更大的新内存空间。随后,基于cache(缓存)中压缩的内容生成Transaction_payload_log_event,并设置压缩前后的相关元信息,函数的主要流程如下所示。


        bool binlog_cache_data::compress(THD *thd) {
            ...
            // binlog_transaction_compression参数值判断
            if (thd->variables.binlog_trx_compression == falsegoto end;
            ...
           //校验是否存在非压缩场景
            if (has_incident()) goto end;
            if (thd->get_transaction()->has_modified_non_trans_table(
                  Transaction_ctx::STMT) ||
              thd->get_transaction()->has_modified_non_trans_table(
                  Transaction_ctx::SESSION))
                  goto end;
            if (may_have_sbr_stmts()) goto end;
            ...
            //获取压缩器,并检查是否需要申请更大内存空间
            if ((compressor = cctx.get_compressor(thd)) == nullptrgoto end;
            ...
            Transaction_payload_log_event tple{thd};
            ctype = compressor->compression_type_code();
            compressor->open();
            stream.set_compressor(compressor);
            //将cache中的数据拷贝压缩
            if ((error = m_cache.copy_to(&stream))) goto compression_end;
            compressor->close();


            //基于压缩后的数据生成Transaction_payload_log_event的内容
            std::tie(buffer, size, std::ignore) = compressor->get_buffer();
            tple.set_payload((const char *)buffer);
            //设置压缩后数据的大小
            tple.set_payload_size(size);
            //设置压缩方式
            tple.set_compression_type(ctype);
            //设置压缩前的数据大小
            tple.set_uncompressed_size(uncompressed_size);
            //将event写入cache中
            error = write_event(&tple);
            //释放新申请buffer空间,压缩器中重置为旧buffer空间
            if (old_buffer) {
                std::tie(buffer, std::ignore, std::ignore) = compressor->get_buffer();
                compressor->set_buffer(old_buffer, old_capacity);
                free(buffer);
            }
        }

        备机接收到Transaction_payload_log_event后,会将其写入RelayLog中,并由worker或者applier线程负责解析回放binlog中的各个log event(日志事件),核心处理逻辑如下所示。


          int Transaction_payload_log_event::do_apply_event(Relay_log_info const *rli) {
            ...
             //依次解压缩并处理事务中包含的各个log event
            for (auto ptr : it) {
              ...
              if ((res = apply_payload_event(rli, (const uchar *)ptr))) break;
              ...
            }
            ...
          }


          bool Transaction_payload_log_event::apply_payload_event(
              Relay_log_info const *rli, const uchar *event_buf) {
            ...
            if (binlog_event_deserialize(ptr, event_len, &fdle, true, &ev)) {
              res = true;
              goto end;
            }
            ...
            if (is_mts_worker(thd)) {
                ...
                //并行方式,由worker线程回放
                res = ev->do_apply_event_worker(worker);
            } else {
                ...
               //非并行方式,由applier线程回放
               res = ev->apply_event(coord);
            }
            ...
          }

          此外,利用mysqlbinlog具解析带有压缩事务的binlog文件时,相关调用路径也类似回放时,需要依次遍历解析并打印压缩事务中的各个log event

            void Transaction_payload_log_event::print(FILE *,
                 PRINT_EVENT_INFO *info) const {
               ...
              //打印压缩事务的起始格式
               if (!info->short_form) {
                std::ostringstream oss;
                oss << "\tTransaction_Payload\t" << to_string() << std::endl;
                oss << "# Start of compressed events!" << std::endl;
                print_header(head, info, false);
                my_b_printf(head, "%s", oss.str().c_str());
               }


              //遍历并依次打印事务中各个log event
              binary_log::transaction::compression::Iterable_buffer it(
                  m_payload, m_payload_size, m_uncompressed_size, m_compression_type);
              for (auto ptr : it) {
                  ...
                  if (binlog_event_deserialize((const unsigned char *)buffer, event_len,
                                             &glob_description_event, true, &ev))
                  ...
                  process_event(info, ev, header()->log_pos, ""true);
              }
              ...
             //打印压缩事务的结束格式
              if (!info->short_form) my_b_printf(head, "# End of compressed events!\n");
              ...
            }

            3


            使用方式 &效果性能验证


            binlog压缩相关控制变量具体描述见表1。 


            表1 binlog压缩相关参数说明


            同时,可通过如下所示的show variables命令查看当前参数值,通过set命令进行参数值的更新。

             

              mysql> show global variables like '%binlog_transaction_compression%';
              +-------------------------------------------+-------+
              | Variable_name                             | Value |
              +-------------------------------------------+-------+
              | binlog_transaction_compression            | OFF   |
              | binlog_transaction_compression_level_zstd | 3     |
              +-------------------------------------------+-------+
              2 rows in set (0.02 sec)
                //开启binlog压缩
                mysql> set global binlog_transaction_compression = on;
                Query OK, 0 rows affected (0.00 sec)


                //根据需要,调整zstd压缩等级
                mysql> set global binlog_transaction_compression_level_zstd = 7;
                Query OK, 0 rows affected (0.00 sec)


                压缩比验证:


                创建8U32G的实例,使用sysbench导入250张表,每张表包含25000行数据。开启32个压测线程,在混合读写模型(oltp_read_write)下多次开启压缩/非开启压缩,进行长达60s的压测,对比压测结束后binlog文件大小。


                结果显示,压缩比(压缩前/压缩后约为2binlog压缩功能表现出较明显的压缩效果


                  binlog_transaction_compression_level_zstd = 3
                  //未开启压缩binlog文件大小,354.21MB
                  //开启压缩后binlog文件大小,171.50MB


                  性能验证:


                  创建8U32G的实例,使用sysbench导入250张表,每张表包含25000行数据。开启32个压测线程,设置压缩级别binlog_transaction_compression_level_zstd3,在混合读写模型(oltp_read_write)下多次开启压缩/非开启压缩,完成压测后取平均值数据。


                  结果显示,基于上述场景,压缩后相对于压缩前性能劣化约3%


                  4


                  总结


                  基于zstd压缩算法的binlog压缩功能,以较小的性能劣化,实现了存储开销减半,并节省了网络带宽。该功能在绝大多数实际使用场景中表现出较好的应用效果。


                  参考资料

                  [1] https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-20.html

                  [2] MySQL binlog 压缩功能对性能的影响,https://blog.csdn.net/weixin_39629075/article/details/113557347

                  [3] 在降本增效背景下谈MySQL压缩功能,https://zhuanlan.zhihu.com/p/629972777


                  END


                  华为云数据库 新用户


                  Flexus云数据库RDS
                  6个月366元(原价1200)/

                  一年722.4元(原价2400)

                  RDS for MySQL单机版  1年729.12元


                  扫码抢购

                         

                  活动时间:2025/4/15-2025/5/31

                    戳“阅读原文”,了解更多

                  文章转载自GaussDB数据库,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                  评论