写点什么

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

  • 2025-04-30
    广东
  • 本文字数:3847 字

    阅读完需:约 13 分钟

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

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


该函数首先会判断 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。


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。


同时,可通过如下所示的 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 文件大小。


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


binlog_transaction_compression_level_zstd = 3未开启压缩binlog文件大小,1.84 GB开启压缩后binlog文件大小,0.89 GB
复制代码


性能验证:


创建 8U32G 的实例,使用 sysbench 导入 250 张表,每张表包含 25000 行数据。开启 32 个压测线程,设置压缩级别 binlog_transaction_compression_level_zstd 为 3,在混合读写模型(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


关注“华为云开发者联盟”,了解更多技术动态。

用户头像

提供全面深入的云计算技术干货 2020-07-14 加入

生于云,长于云,让开发者成为决定性力量

评论

发布
暂无评论
【华为云MySQL技术专栏】Binlog压缩:节省存储,优化网络,提升性能_华为云_华为云开发者联盟_InfoQ写作社区