PolarDB MySQL版Binlog大事务优化方案

开启Binlog后,如果一个事务需要记录大量的Binlog,在该事务提交时会阻塞其它需要提交的事务记录Binlog,下文中将这类事务简称为大事务。为了解决这个问题,PolarDB MySQL版优化了这种事务的提交过程,以提高系统稳定性。

背景信息

开启Binlog后,事务在提交时需要记录Binlog,该过程如下图所示。各个事务在执行过程中会将产生的Binlog临时记录在各自的Binlog Cache结构中。提交事务时,若这些事务同时提交,它们将排队从各自的Binlog Cache结构中读取Binlog并写入Binlog文件中。如果某个正在排队的事务(Trx2)需要写大量的Binlog,例如,Binlog的规模为1 GB,写Binlog文件的时间长,导致这段时间后面排队的事务也无法写Binlog文件,因而无法完成提交。因此,系统在这段时间不可写,导致出现了大量的慢SQL,进而可能导致客户端重试或连接暴涨等问题,从而进一步加大系统压力。

image

PolarDB MySQL版针对性地推出大事务优化方案来解决该问题,使用该优化方案后,大事务写Binlog不再阻塞其它写Binlog事务的提交,避免系统短时间不可写入,从而产生大量写请求慢SQL或连接暴涨等问题。

大事务优化方案

方案概览

如下图所示,对比上方的方案,该优化方案将大事务(Trx2)写的Binlog Cache文件直接重命名为下一个Binlog文件。即Trx2写Binlog的过程被分摊到事务执行过程中,Trx2提交时只需要执行一次迅速的重命名操作。期间虽然会拿锁切换Binlog文件,阻塞其它事务的写入。但该过程拿锁时间很短(小于0.01秒),所以,其它事务的提交操作基本不再受Trx2写Binlog文件的影响。

image

将Binlog Cache文件重命名为Binlog文件

该优化的设计核心是如何将Binlog Cache文件重命名为Binlog文件。Binlog Cache文件不同于Binlog文件,其中不包含Binlog文件头,如Magic Header、Format Description Event和Previous GTID Event等。也不包含一些事务信息,如GTID Event。且每个Binary Log Event的Header都会记录其在文件中的offset等信息。因此,有两个问题需要解决:

  • 如何在重命名时填充Binlog头信息和事务信息?

    为了能直接由Binlog Cache文件重命名得到Binlog文件,需要在Binlog Cache文件头预留一个空间来填充这些信息。后续Binary log写入Binlog Cache也需要根据预留空间大小重新计算offset信息。

  • 如何确定预留空间的大小?

    因为需要填充的Binlog头信息和事务信息数据量不固定,而Binlog文件又不允许空洞,所以该设计采用固定的预留空间。在填充完Binlog头信息和事务信息后,在预留空间剩余的部分填充ignorable log event。

整个重命名过程如下图所示:

image
重要

下游复制节点无法使用以database并发方式的Multi-Threaded Replication,但可以使用logical clock并发方式,禁止指定slave_parallel_workers>0, slave_parallel_type='DATABASE',但允许指定slave_parallel_workers> 0, slave_parallel_type= 'logical_clock'

说明

对大事务所在的Binlog文件禁用checksum,其它Binlog文件不受影响:在Binlog文件中,是否记录每个event的checksum取决于全局参数binlog_checksum_options。而大事务从写Binlog Cache开始到结束期间,参数binlog_checksum_options的值可能会发生变化,所以无法确定是否开启checksum。

小结

Binlog大事务优化方案的关键是将大事务在提交时需要排队写Binlog文件的过程提前到事务提交之前。优化前,在事务提交时需要将大量Binary logs写入云盘。优化后,只需要将Binlog Cache文件重命名为新的Binlog文件。大大缩短了大事务提交的时间,从而缩短了其它事务的等待时间。