大事务提交优化

在有大事务的业务场景中,大事务提交时常会出现Binlog耗时过长、实例长时间不可写或夯住(hang)的问题。RDS MySQL引入的Binlog Cache Free Flush功能,优化了大事务提交的速度,解决了因大事务提交时间过长导致的实例抖动、长时间不可写的问题,提升了实例的稳定性。

功能说明

由于Binlog的串行写入机制,大事务不仅自身写入Binlog缓慢,还会阻塞其他事务提交。本功能优化了大事务提交时的Binlog写入机制,使提交速度不再受Binlog Event数量影响,始终保持高效。本功能解决了以下典型问题:

  • 超大事务阻塞实例,导致其他事务无法及时提交。

  • 大事务引发大量IO消耗,造成IO资源耗尽并拖慢查询等业务。

  • 大事务导致小事务执行变慢,引发活跃连接激增和性能抖动,严重时可能触发雪崩效应导致实例不可用。

  • 频繁执行大事务持续引发业务流量的性能抖动。

说明
  • 这里大事务是指产生的Binlog Event量大的事务,详见问题分析

  • Binlog Cache Free Flush功能的原理详见实现原理

前提条件

实例版本及内核小版本要求:

  • MySQL 8.0,小版本为20240731或以上。

  • MySQL 5.7,小版本为20240731或以上。

    说明

    您可以在基本信息页面的配置信息区域查看是否有升级内核小版本按钮。如果有按钮,您可以单击按钮查看当前版本;如果没有按钮,表示已经是最新版。详情请参见升级内核小版本

使用说明

  • 新建实例:Binlog Cache Free Flush功能默认开启。

  • 存量实例:通过设置全局系统变量loose_binlog_cache_free_flush可开启该功能,参数修改后立即生效,无需重启实例。

  • 相关参数:

    loose_binlog_cache_free_flush_limit_size:开启Binlog Cache Free Flush功能后,当事务的Binlog超过该参数值,提交时会将Binlog Cache临时文件转为Binlog正式文件。默认值:256 MB,取值范围: 20971520~18446744073709551615 (单位:字节)。

问题分析

MySQL实例执行大事务时,慢查询日志可能记录到某些本应快速完成的语句(如COMMIT)出现异常延迟。image

原因是另一个线程的大事务因Binlog串行写入机制导致资源竞争。大事务不仅自身写Binlog耗时较长,还会阻塞其他事务的Binlog提交,引发全局串行化延迟。

运行sysbench oltp_write_only时,在每5秒执行一次产生512 MB Binlog的大UPDATE操作后,观察到以下现象:

  • 每次大UPDATE提交时,TPS(每秒事务数)剧烈下降。

  • 抖动时长与Binlog大小正相关:事务产生的Binlog越大,对其他业务的延迟影响越显著。

下图直观的展示了大事务对线上运行业务的影响。

image

UPDATE相关操作:

CREATE TABLE t_large (a INT, b LONGTEXT) ENGINE = InnoDB;

# 插入一行数据包含256 MB文本,UPDATE时产生的Binlog events是512 MB。
INSERT INTO t_large VALUES (1, repeat('a', 256000000)); 

UPDATE t_large SET a = a + 1;

优化效果

  • TPS稳定性显著提升:

    • 优化前:每5秒一次的大UPDATE导致TPS剧烈波动。

    • 优化后(橙色线):大UPDATETPS的影响几乎消除。

    image

  • 延迟无明显波动:

    • 不同大小的UPDATE操作对时延的影响不再显著(橙色线为优化后)。

    • 即使Binlog Events超过512 MB,其他事务提交的延迟未受明显干扰。

    image

实现原理

Binlog Cache介绍

image

Binlog Cache是一块会话级别的临时空间,即为每一个会话建立一个Binlog Cache,用来暂存Binlog events。Binlog Cache由内存缓存(Buffer)和临时文件(Temp File)两部分组成,其中内存缓存的大小由binlog_cache_size参数控制。当事务比较大,内存缓存被写满之后,事务的events就会被记录到临时文件中。

事务在执行过程中,会将自己生成的events暂存在Binlog Cache中。事务在提交时,需要按顺序从Binlog Cache中读出一个个Event,并且更新eventsend_pos(结束位置)和checksum(校验和),然后将其写到Binlog文件中。为了保证事务在Binlog文件中的连续性,整个过程是加锁进行的,即一个事务写Binlog文件时,其他事务都会被阻塞。

大事务写Binlog带来的问题

image

对于非常大的事务,其使用的Binlog Cache可能会达到几十个GB,在提交事务期间写Binlog的过程耗时很长,并且对实例影响极大,主要包括以下两点:

  • 大事务写Binlog过程中,会持有Binlog写锁,期间整个实例将处于不可写状态。

  • 大事务写Binlog的过程会消耗大量IO资源,在IO资源有限的场景下,可能导致整个实例夯住(hang)。

Binlog Cache Free Flush优化

image

AliSQLBinlog Cache的临时文件进行了改造,使其具备了直接转为Binlog文件的能力。开启本功能后,在大事务提交时会将Binlog Cache临时文件直接转化为Binlog正式文件,这个过程的耗时很短,并且IO资源消耗很少,彻底解决了大事务写Binlog对实例的影响。

Binlog Cache的改造

image

为了使Binlog Cache的临时文件能够直接转化为Binlog正式文件,AliSQLBinlog Cache的使用方式进行了改造。主要包括以下两点:

  • 在写Binlog Cache的临时文件时,在文件头部预留出一部分空间。在临时文件转Binlog正式文件过程中,这部分空间用于存放Binlog头部的events。

  • 在写Binlog Cache时,每个events都根据预留空间的大小计算自己的end_pos。

Binlog文件的转化

image

在大事务提交时,会直接将Binlog Cache临时文件转化为Binlog正式文件。在这个过程中,Binlog的文件头(File Header )和头部的几个events(Header Events)会填满预留空间,预留空间主要包含四部分内容:

  • File Header:在每个Binlog文件的头部4字节,都写有一个标识:[ 0xFE 'bin'],用来标识此文件是个Binlog文件。

  • Header Events:包括Format description eventPrevious gtid event。

  • Empty event:用来填充剩余的预留空间的,使用当前Binlog中已有的ignorable event类型。

  • Gtid event:大事务的Gtid是在提交阶段生成的,Gtid event也会被写到预留空间中。

预留空间之后的Events in Binlog Cache,是Binlog Cache中的内容,主要包括Query event、Table map event、Row eventXid event等。

与普通的Binlog文件相比,使用本功能转化而来的Binlog文件有两点区别:

  • 多了一个Empty event。

  • 默认关闭了本文件的checksum。

Binlog Cache Free Flush功能不会改变Binlog的格式,基于Binlog的复制及第三方的工具不会受任何影响。