为什么需要并行复制
在 MySQL 主从架构中,主库的写入操作是并发的——多个事务可以同时执行。但从库的 SQL 线程在很长一段时间里只有一个,所有来自 relay log 的事务必须串行回放。这就导致了一个经典问题:主库写入压力稍大,从库就开始延迟,而且延迟会持续累积。
MySQL 5.6 引入了按库(schema)并行的雏形,5.7 基于组提交(group commit)实现了更细粒度的并行,到了 8.0 又通过 writeset 机制进一步优化。理解这些机制的演进和底层原理,是面试中区分候选人对复制理解深度的关键分水岭。
并行复制的基本模型
从库的复制流程涉及两类线程:
- IO 线程:从主库拉取 binlog,写入本地 relay log。
- SQL 线程(worker 线程):从 relay log 中读取事件并在从库回放。
并行复制的核心思想是:把原本由一个 SQL 线程串行完成的工作,分发给多个 worker 线程并发执行。但这里有一个硬性约束——不能破坏事务之间的依赖关系。如果两个事务在主库上是有先后依赖的(比如都修改同一行),从库回放时也必须保持顺序,否则数据就会不一致。
因此,并行复制的本质问题是:如何判断哪些事务可以安全地并行回放?
三种并行策略的演进
按库并行(MySQL 5.6)
最早的方案是 slave_parallel_type=DATABASE。它的逻辑很简单:不同数据库的事务可以并行,同一个数据库的事务必须串行。
这个策略的局限性非常明显——如果业务只用一个库(这在互联网公司几乎是标配),所有事务都落在同一个 database 上,并行度直接退化为 1。所以按库并行在实际生产中很少真正解决问题。
基于组提交的并行(MySQL 5.7)
MySQL 5.7 引入了 slave_parallel_type=LOGICAL_CLOCK,这是真正意义上可用的并行复制。
它的原理基于主库的组提交机制。在主库上,一组能够同时提交的事务(在 prepare 阶段已经完成、等待 commit 的事务)之间不存在冲突,因为它们已经在锁层面被证明可以并发执行。MySQL 在 binlog 中为每个事务记录了它所属的组信息(通过 last_committed 和 sequence_number 两个字段)。
从库的协调线程(coordinator)读取这些信息后,知道哪些事务属于同一组,就可以把它们分发给不同的 worker 并行回放。同一组内的事务并行执行,不同组之间保持顺序。
这个方案的关键前提是主库的 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 参数配置合理。如果主库组提交的组很小(比如每组只有一两个事务),并行度就会很低。
基于 writeset 的并行(MySQL 8.0)
MySQL 8.0 进一步引入了 binlog_transaction_dependency_tracking=WRITESET。它的思路是:不再依赖组提交的时机,而是直接在主库上计算每个事务修改了哪些行(以 hash 形式记录为 writeset),只要两个事务的 writeset 没有交集,就可以并行回放。
这解决了组提交方案的一个核心痛点——即使主库并发不高、组提交没有形成大组,只要事务之间确实不冲突,从库仍然可以并行。对于高并发但冲突较少的业务场景,writeset 方案能显著提升并行度。
slave_parallel_workers 调优实践
slave_parallel_workers 决定了 worker 线程的数量,设为 0 表示关闭并行复制。调优时需要综合考虑以下几个维度:
设置多少合适
这不是一个拍脑袋的数字。基本参考原则:
- 不超过从库 CPU 核心数。worker 线程是 CPU 密集型操作,超过核心数只会增加上下文切换开销。
- 观察主库的并发写入特征。如果主库并发事务数本身就不高,设置再多 worker 也无济于事,因为协调线程根本没有足够多可并行的事务可以分发。
- 常见起点是 4~8,然后根据实际延迟情况调整。对于写入密集型的 OLTP 场景,8~16 也是合理的。
配套参数不能忽略
单独调 slave_parallel_workers 往往效果有限,以下参数需要一起考虑:
slave_parallel_type:5.7 环境务必设为LOGICAL_CLOCK,不要用默认的DATABASE。binlog_group_commit_sync_delay:在主库上适当设置(如 1000 微秒),可以让更多事务进入同一个组提交,从而增大从库可并行的组大小。代价是主库提交延迟略微增加。binlog_transaction_dependency_tracking:MySQL 8.0 中设为WRITESET或WRITESET_SESSION,让并行判断更精细。slave_preserve_commit_order:开启后(8.0 默认开启),worker 线程会按照主库的提交顺序在从库提交,保证从库上事务的可见顺序与主库一致。对于依赖 binlog 做数据恢复或使用多线程复制的级联场景,这个参数很重要。
监控与验证
调优不能凭感觉,需要关注以下指标:
SHOW SLAVE STATUS中的Seconds_Behind_Master:最直观的延迟指标,但注意它在某些场景下不够准确(比如 IO 线程断开时可能显示为 NULL)。performance_schema.replication_applier_status_by_worker:可以看到每个 worker 的状态和当前执行的事务,判断是否存在某个 worker 长期忙碌而其他空闲的情况。- 主库的
binlog_group_commit相关状态变量:确认组提交是否形成了足够大的组。
如果发现 worker 数量增加后延迟没有改善,通常说明瓶颈不在并行度上,而可能在于:
- 大事务导致单个 worker 长时间执行(需要拆分大事务)。
- 从库磁盘 IO 能力不足。
- 从库上有其他查询争抢资源。
面试中的常见追问
问:并行复制会不会导致从库数据不一致?
不会。并行复制的设计前提就是保证事务依赖关系不被破坏。LOGICAL_CLOCK 依赖主库组提交的冲突检测,WRITESET 依赖行级 hash 冲突检测,两者都确保了有冲突的事务不会被并行执行。加上 slave_preserve_commit_order 保证提交顺序,从库的最终一致性是有保障的。
问:为什么设置了 slave_parallel_workers=16,延迟还是很大?
可能的原因包括:主库组提交组太小导致可并行事务不足;存在大事务(如批量 DELETE/UPDATE)阻塞了单个 worker;从库硬件资源(磁盘 IOPS、CPU)达到瓶颈;或者 slave_parallel_type 没有设置为 LOGICAL_CLOCK。
问:MySQL 8.0 的 WRITESET 相比 5.7 的 LOGICAL_CLOCK 优势在哪?
核心优势是不依赖组提交的时机。LOGICAL_CLOCK 受限于主库组提交能否形成大组,而 WRITESET 直接基于事务修改的行集合判断冲突,即使主库并发不高也能实现较好的并行度。此外 WRITESET 还能处理主库上不同时间提交但实际不冲突的事务,并行判断更加精确。
小结
并行复制的演进本质上是在“并行度”和“一致性”之间寻找更优的平衡点。从按库并行到组提交并行再到 writeset 并行,判断粒度越来越细,对主库并发特征的依赖越来越小。slave_parallel_workers 的调优不是孤立地调一个数字,而是需要结合主库的写入模式、组提交配置、从库硬件资源综合判断。理解原理之后再动手调参,才能做到有的放矢。
未经允许不得转载:任鹏个人博客 » MySQL 中的并行复制原理与 slave_parallel_workers 调优

