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

举报
GaussDB 数据库 发表于 2025/04/25 09:09:21 2025/04/25
【摘要】 一、背景及概述Binlog(二进制日志)记录了数据的更改操作,具有极为重要的作用,可用于数据恢复、数据复制和审计。然而,当用户在使用MySQL时,如果写入业务量过大,可能会导致磁盘空间和网络带宽的过度使用。为了应对这一情况,MySQL 8.0.20版本推出了基于zstd压缩算法的binlog压缩功能。本文将从源码角度对binlog压缩功能进行浅析,帮助读者了解其使用方式。同时,通过测试结果展...

11.jpg

一、背景及概述

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

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

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

二、原理浅析

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,其流程图如下。

22.png

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 == false) goto 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)) == nullptr) goto 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

三、使用方式 & 效果性能验证

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

33.png

表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%

四、总结

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


【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。