并行复制:多线程从库加速方案

并行复制:多线程从库加速方案

概述

传统 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)
 T1T2T3 写入不同的主键  可以并行 

性能提升参考

配置 从库 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;
-- 其他事务必须等大事务完成

面试要点

  1. 复制延迟的主因:主库多线程写入 vs 从库单线程回放
  2. MySQL 5.6 按数据库并行:效果有限
  3. MySQL 5.7 按逻辑时钟并行:基于组提交,效果明显
  4. MySQL 8.0 WRITESET 追踪:更细粒度并行,效果最佳
  5. 推荐配置slave_parallel_workers=8 + LOGICAL_CLOCK + preserve_commit_order=ON
  6. 并行复制只解决 SQL 回放瓶颈,I/O 线程仍是单线程
  7. 大事务是并行复制的天敌
© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容