阿里思想学习-如何优化大事务提交
参考文章:大事务提交优化_云数据库 RDS(RDS)-阿里云帮助中心
介绍一下BinlogCache
binlogCache写入流程介绍
binlog cache:每个客户端连接都有一个线程级别的binlog缓存区,用于临时存储事务产生的binlog事件
事务提交过程:
- 事务执行期间:所有DML操作产生的binlog会先写入binlog cache
- 事务提交时:binlog cache中的内容才会写入磁盘的binlog文件
- 写入后:binlog cache会被清空
大事务导致Binlog Cache写满
事务过大:
- 当单个事务包含大量DML操作(如批量插入/更新数十万行)
- 产生的binlog事件总量超过了binlog cache的容量
Cache无法动态扩展:
- binlog cache大小由
binlog_cache_size
参数决定(默认32KB) - 事务执行期间cache大小固定,不会自动扩容
Cache写满后的行为:
- 当cache空间不足时,MySQL会报错:"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage"
- 事务会被回滚
既然BinlogCache是线程级别的,那为什么说大事务会阻塞其他事务呢?
Binlog Cache写入Binlog的时候要拿到锁,也就是拿到二进制日志全局锁-binlog group commit
拿到这个锁才能往binlog文件写入日志,大事务会导致写日志的时间非常长,从而阻塞同步
其他事务必须等待这个锁释放才能提交,导致所有新事务在提交阶段被阻塞
大事务提交优化
文章来源:
大事务提交优化_云数据库 RDS(RDS)-阿里云帮助中心
什么是大事务
在有大事务的业务场景中,大事务提交时常会出现Binlog耗时过长、实例长时间不可写或阻塞的问题
RDS MySQL引入的Binlog Cache Free Flush功能,优化了大事务提交的速度
解决了因大事务提交时间过长导致的实例抖动、长时间不可写的问题,提升了实例的稳定性
功能说明:
由于Binlog的串行写入机制,大事务不仅自身写入Binlog缓慢,还会阻塞其他事务提交
本功能优化了大事务提交时的Binlog写入机制,使提交速度不再受Binlog Event数量影响,始终保持高效
解决了什么问题?
本功能解决了以下典型问题:
- 超大事务阻塞实例,导致其他事务无法及时提交
- 大事务引发大量IO消耗,造成IO资源耗尽并拖慢查询等业务
- 大事务导致小事务执行变慢,引发活跃连接激增和性能抖动,严重时可能触发雪崩效应导致实例不可用。
- 频繁执行大事务持续引发业务流量的性能抖动
使用说明
使用说明:
- 新建实例: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)出现异常延迟
原因是另一个线程的大事务因Binlog串行写入机制导致资源竞争
大事务不仅自身写Binlog耗时较长,还会阻塞其他事务的Binlog提交,从而引发全局串行化延迟
运行sysbench oltp_write_only时,在每5秒执行一次产生512 MB Binlog的大 UPDATE 操作后,观察到以下现象:
- 每次大UPDATE提交时,TPS(每秒事务数)剧烈下降
- 抖动时长与Binlog大小正相关:事务产生的Binlog越大,对其他业务的延迟影响越显著
下图直观的展示了大事务对线上运行业务的影响
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;
实现原理
Binlog Cache介绍
Binlog Cache是一块会话级别的临时空间
即为每一个会话建立一个Binlog Cache,用来暂存Binlog events
Binlog Cache由内存缓存(Buffer)和临时文件(Temp File)两部分组成,其中内存缓存的大小由binlog_cache_size参数控制
当事务比较大,内存缓存被写满之后,事务的events就会被记录到临时文件中
事务在执行过程中,会将自己生成的events暂存在Binlog Cache中
事务在提交时,需要按顺序从Binlog Cache中读出一个个Event,并且更新events的end_pos(结束位置)和checksum(校验和),然后将其写到Binlog文件中
为了保证事务在Binlog文件中的连续性,整个过程是加锁进行的,即一个事务写Binlog文件时,其他事务都会被阻塞
大事务写Binlog带来的问题
对于非常大的事务,其使用的Binlog Cache可能会达到几十个GB,在提交事务期间写Binlog的过程耗时很长,并且对实例影响极大,主要包括以下两点:
- 大事务写Binlog过程中,会持有Binlog写锁,期间整个实例将处于不可写状态
- 大事务写Binlog的过程会消耗大量IO资源,在IO资源有限的场景下,可能导致整个实例夯住(hang)
Binlog Cache Free Flush优化
AliSQL对Binlog Cache的临时文件进行了改造,使其具备了直接转为Binlog文件的能力
开启本功能后,在大事务提交时会将Binlog Cache临时文件直接转化为Binlog正式文件
这个过程的耗时很短,并且IO资源消耗很少,彻底解决了大事务写Binlog对实例的影响
对Binlog Cache的改造
为了使Binlog Cache的临时文件能够直接转化为Binlog正式文件,AliSQL对Binlog Cache的使用方式进行了改造。主要包括以下两点:
- 在写Binlog Cache的临时文件时,在文件头部预留出一部分空间。在临时文件转Binlog正式文件过程中,这部分空间用于存放Binlog头部的events
- 在写Binlog Cache时,每个events都根据预留空间的大小计算自己的end_pos
阿里SQL的创新解决方案
阿里云的优化思路是:让临时文件可以直接"变身"为正式binlog文件,省去中间的读取-计算-写入过程
关键设计:"预留头部空间"
- 预留请求头空间的作用:
在创建binlog cache临时文件时,预先在文件开头保留一定空间
这个空间专门用来存放binlog文件的标准头部信息
- 写入events时的特殊处理:
每个event写入临时文件时,其位置计算会考虑预留空间的大小
即实际写入位置 = 逻辑位置 + 预留空间大小
这样event之间的相对位置关系保持不变
- 提交时的转换过程:
当需要将临时文件转为正式binlog时:
在预留空间写入标准的binlog文件头
修改文件元数据(如inode信息)将其标记为正式binlog文件
更新binlog索引文件
整个过程几乎不涉及数据移动,只是【重命名+补头】
为什么这个设计能提升性能
- 避免数据拷贝:
传统方式需要读取GB级临时文件并重新写入
新方案直接重用已有文件,节省大量IO
- 减少计算开销:
不需要重新计算每个event的end_pos和checksum
因为event位置在写入时已经考虑了预留空间
- 缩短锁持有时间:
转换过程极快(毫秒级)
大幅减少binlog写锁的持有时间
类比解释
可以把这个过程想象成写一封信:
传统方式:
- 先在草稿纸上写完整封信
- 然后重新在正式信纸上誊写一遍
- 最后加上正式信件的抬头和落款
阿里云优化方式:
- 先在纸上预留出抬头的位置
- 直接从预留位置之后开始写正文
- 完成后只需补上抬头
- 把整张纸直接作为正式信件
这种【预留空间+原地转换】的设计,是大事务优化性能提升的关键所在
大事务优化-简单总结
大事务的4个问题:
超大事务阻塞实例,导致其他事务无法及时提交
大事务引发大量IO消耗,造成IO资源耗尽并拖慢查询等业务
大事务导致小事务执行变慢,引发活跃连接激增和性能抖动,严重时可能触发雪崩效应导致实例不可用。
频繁执行大事务持续引发业务流量的性能抖动
Binlog Cache:
事务进行时是用Binlog Cache暂存的
当事务写完要提交的时候,就要写入Binlog文件,写入Binlog文件是要争夺Binlog写锁然后串行执行的
所以大事务会阻塞其他事务的持久化和提交
大事务还会造成大量的磁盘IO,给CPU带来压力
由于Binlog的加锁和串行写入机制,大事务不仅自身写入Binlog缓慢,还会阻塞其他事务提交
当事务比较大,内存缓存被写满之后,事务的events就会被记录到临时文件中
事务在执行过程中,会将自己生成的events暂存在Binlog Cache中
事务在提交时,需要按顺序从Binlog Cache中读出一个个Event,并且更新events的end_pos(结束位置)和checksum(校验和),然后将其写到Binlog文件中
阿里的RDS对Binlog Cache的改造:
阿里的RDS的Binlog Cache Free Flush功能优化了大事务提交时的Binlog写入机制,使提交速度不再受Binlog Event数量影响,始终保持高效
AliSQL对Binlog Cache的临时文件进行了改造,使其具备了直接转为Binlog文件的能力
大事务提交时会将Binlog Cache临时文件直接转化为Binlog正式文件
这个过程的耗时很短,并且IO资源消耗很少,彻底解决了大事务写Binlog对实例的影响
通过【预留空间+原地转换】优化
传统方式需要读取GB级临时文件并重新写入,而新方案直接重用已有文件节省大量IO,从而让写锁占用时间也减少了