Binlog Group Commit
1. 背景:没有组提交时的问题(MySQL 5.7及更早版本的痛点)
在早期版本的MySQL中,为了保证二进制日志(Binlog) 和 存储引擎(如InnoDB) 之间的一致性(即一个事务在Binlog和InnoDB中要么都提交,要么都不提交),MySQL使用了两阶段提交(2PC) 协议,并伴有一个严重的性能瓶颈。
简单流程如下:
准备阶段(Prepare Phase):存储引擎(InnoDB)将该事务的状态标记为“准备就绪”(已写入redo log,但未提交)。
写Binlog阶段:将事务的Event写入Binlog文件的缓存中。
Sync Binlog阶段:调用
fsync()
系统调用,将Binlog缓存强制刷盘。这是最耗时的I/O操作。提交阶段(Commit Phase):存储引擎(InnoDB)将该事务正式提交。
这里的核心问题是: 为了保证事务在Binlog中的写入顺序与在InnoDB中的提交顺序严格一致(这对于复制和恢复至关重要),MySQL在步骤2和3期间必须持有 LOCK_log
和 LOCK_sync
这样的全局互斥锁(Mutex)。
这意味着:
每个事务在写Binlog和刷盘Binlog时,都必须串行化进行。
即使有100个事务已经准备好了,它们也必须一个一个地排队,获取锁,执行耗时的
fsync()
操作,然后释放锁。
fsync()
是一个昂贵的磁盘I/O操作,让每个事务都单独执行一次 fsync()
,相当于把数据库的最高吞吐量限制在了磁盘的IOPS(每秒输入输出次数)上,这在高并发的写场景下是灾难性的,CPU会空转等待I/O,性能急剧下降。
2. Binlog Group Commit(组提交)的核心思想
组提交的精髓可以概括为:一次获取,一组处理
。
它将一个高频率的、单个的 fsync()
操作,打包成一个低频率的、批量的 fsync()
操作。用一次I/O操作的代价,完成多个事务的持久化,极大地提升了效率。
3. 组提交的工作流程(三个阶段)
组提交将提交过程分解为三个顺序的阶段,每个阶段都有一个队列。当一个线程进入流程时,它首先尝试成为当前阶段的领导者(Leader),如果不是领导者,它就加入队列等待。领导者负责代表整个队列执行该阶段的操作。