并行复制:多线程从库加速方案
概述
传统 MySQL 复制中,从库的 SQL 线程是单线程执行的。主库可以通过多个连接并发写入,但从库只有一个 SQL 线程串行回放 Binlog,这就产生了复制延迟。并行复制(Parallel Replication / Multi-Threaded Slave)通过在从库使用多个 SQL 线程回放 Binlog,大幅减少复制延迟。
问题的根源
主库(多线程写入) 从库(单线程回放)
│ │
T1: UPDATE A SQL 线程:串行回放
T2: INSERT B 顺序:T1 → T2 → T3
T3: UPDATE C TPS:受限于单线程能力
│ │
主库 TPS = 10000 从库 TPS = 3000
│ │
└──── 延迟增长 ────────────→
配置并行复制
[mysqld]
# 启用并行复制
slave_parallel_workers = 8 -- 并行 SQL 线程数(默认 0 = 单线程)
slave_parallel_type = LOGICAL_CLOCK -- 并行策略(MySQL 8.0 默认)
# 更多参数
slave_pending_jobs_size_max = 128M -- 并行队列缓存上限
slave_preserve_commit_order = ON -- 保持提交顺序(推荐 ON)
MySQL 并行复制的发展
MySQL 5.6:按数据库并行
最早期的并行复制,以数据库为单位分组:
-- 配置
slave_parallel_type = DATABASE
-- 并行条件:不同数据库的事务可以并行执行
-- T1: UPDATE db1.users (库 db1)
-- T2: INSERT db2.orders (库 db2)
-- → T1 和 T2 可以并行 ✅
-- T3: UPDATE db1.products (库 db1)
-- T4: INSERT db1.logs (库 db1)
-- → T3 和 T4 串行 ❌(同一数据库)
缺点:同一库只能串行,大多数场景一个库的多个表无法并行,效果有限。
MySQL 5.7:按逻辑时钟并行(LOGICAL_CLOCK)
引入组提交(Group Commit)的概念:
slave_parallel_type = LOGICAL_CLOCK
原理:主库中同组提交的事务(last_committed 相同)互不冲突,从库可以并行回放。
主库阶段:
T1: 提交 (last_committed=0, sequence_number=1)
T2: 提交 (last_committed=0, sequence_number=2) ← 同组
T3: 提交 (last_committed=2, sequence_number=3)
T4: 提交 (last_committed=2, sequence_number=4) ← 同组
↑
Binlog 组提交
从库回放:
第一波:T1 和 T2 并行执行 ✅
第二波:T1、T2 完成后,T3、T4 并行执行 ✅
关键参数:
# 主库:控制组提交时的等待时间(让更多事务进入同组)
binlog_group_commit_sync_delay = 1000000 -- 微秒(1 秒)
binlog_group_commit_sync_no_delay_count = 10 -- 最多等 10 个事务
MySQL 8.0:基于 WRITESET 的并行复制
进一步优化,更细粒度地判断事务能否并行:
slave_parallel_type = LOGICAL_CLOCK
# 主库额外配置(8.0 新增)
binlog_transaction_dependency_tracking = WRITESET -- 基于行写入集合
WRITESET 原理:跟踪每个事务写入的行的主键(或唯一键),如果两个事务写入不同的行,即使是在同一数据库同一表,也被判定为无冲突,可以并行回放。
T1: UPDATE users SET balance=100 WHERE id=1 (写入主键 id=1)
T2: UPDATE users SET balance=200 WHERE id=2 (写入主键 id=2)
T3: UPDATE users SET balance=300 WHERE id=3 (写入主键 id=3)
→ T1、T2、T3 写入不同的主键 → 可以并行 ✅
性能提升参考
| 配置 | 从库 TPS | 对比单线程 |
|---|---|---|
单线程(slave_parallel_workers=0) |
3,000 | 1x |
| 4 线程 DATABASE | 4,000 | 1.3x |
| 4 线程 LOGICAL_CLOCK | 8,000 | 2.7x |
| 8 线程 LOGICAL_CLOCK + WRITESET | 12,000 | 4x |
配置建议
# MySQL 8.0 推荐配置
slave_parallel_workers = 8
slave_parallel_type = LOGICAL_CLOCK
slave_preserve_commit_order = ON -- 保持提交顺序,防止数据不一致
slave_pending_jobs_size_max = 256M
# 主库(开启 WRITESET 追踪)
binlog_transaction_dependency_tracking = WRITESET
注意事项
1. 保持提交顺序(preserve_commit_order)
slave_preserve_commit_order = ON
确保从库的提交顺序与主库一致,这在启用并行复制后尤为重要。如果关闭,从库的最终数据可能不同。
2. 并非线程越多越好
- 线程数建议:CPU 核心数的一半或等量
- 线程太多会导致线程上下文切换开销超过并行收益
- 从库的内存也需要相应增加
3. IO 线程仍是单线程
并行复制只解决了 SQL 回放的问题。从库的 I/O 线程(拉取 Binlog 写入 Relay Log)仍然是单线程的。
4. 大事务问题
-- 大事务会导致并行度下降
INSERT INTO logs SELECT * FROM huge_table;
-- 其他事务必须等大事务完成
面试要点
- 复制延迟的主因:主库多线程写入 vs 从库单线程回放
- MySQL 5.6 按数据库并行:效果有限
- MySQL 5.7 按逻辑时钟并行:基于组提交,效果明显
- MySQL 8.0 WRITESET 追踪:更细粒度并行,效果最佳
- 推荐配置:
slave_parallel_workers=8+LOGICAL_CLOCK+preserve_commit_order=ON - 并行复制只解决 SQL 回放瓶颈,I/O 线程仍是单线程
- 大事务是并行复制的天敌
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容