并行复制通过多SQL线程并发回放互不干扰的事务绕过单点锁竞争,MySQL 5.6基于库(DATABASE)分发,5.7+支持LOGICAL_CLOCK组提交并行;但需主库ROW格式、避免DDL和跨库事务,并配合拆分大事务等主库优化才能真正缓解锁竞争。

并行复制如何绕过SQL线程单点锁竞争
MySQL 5.6 以前,从库只有一个 SQL thread 串行回放 relay log,所有事务(哪怕修改的是完全无关的表或库)都得排队等锁、等事务提交、等 InnoDB 行锁释放。这就导致一个慢事务(比如大 UPDATE)会卡住后续所有事务的执行,形成“锁链式阻塞”。并行复制的本质不是让单个事务变快,而是让**互不干扰的事务能真正并发执行**——只要它们不争抢同一资源,就无需排队。
并行复制依赖的分发策略决定锁竞争是否被隔离
MySQL 并行复制(MTS, Multi-Threaded Slave)能否缓解锁竞争,取决于你启用的并行策略:
-
slave_parallel_type = DATABASE:按库分发,同一库的所有事务强制由同一个 worker thread 执行。如果业务集中在少数几个库(如user_db、order_db),那这些库内部仍会串行,锁竞争照旧; -
slave_parallel_type = LOGICAL_CLOCK(MySQL 5.7+ 默认):基于组提交(group commit)信息,把主库上能并行提交的事务,在从库也尽可能并行回放。前提是主库开启了binlog_group_commit_sync_delay或有真实并发写入,否则从库看到的仍是“伪并行”; - 使用 GTID +
slave_parallel_workers > 0且主库 binlog_format = ROW:这是目前最稳妥的组合,能更精细地识别事务间无冲突的数据变更,worker thread 分配更合理。
为什么开了并行复制,SHOW PROCESSLIST 里还是看到大量 Waiting for table metadata lock
这不是并行复制失效,而是你没意识到:元数据锁(MDL)是库/表级别的,且由 coordinator thread 统一协调。即使多个 worker thread 在并行执行不同事务,只要其中任意一个事务需要 ALTER TABLE、TRUNCATE 或 DDL,就会触发全局 MDL 等待,把所有 worker 都卡住。常见诱因包括:
- 运维脚本在从库误执行了 DDL(禁止!);
- 主库有长事务未提交,其持有的 MDL 会通过 binlog 传播到从库,导致后续所有涉及该表的操作等待;
-
slave_preserve_commit_order = ON(尤其在LOGICAL_CLOCK模式下)会强制某些事务按序提交,人为引入串行点。
真正缓解锁竞争,还得配合主库侧改造
只改从库并行参数治标不治本。锁竞争根子常在主库:
- 拆分大事务:把
UPDATE t1 WHERE id BETWEEN 1 AND 1000000改成每次 1000 行 +COMMIT,减少单事务持有锁时间; - 避免跨库事务:MySQL 的
DATABASE级并行对跨库事务无效,这类事务会被 coordinator 强制降级为单线程回放; - 确认主库 binlog_format 是
ROW:STATEMENT 格式下,函数、临时表、非确定性语句可能导致从库执行时锁范围扩大或顺序错乱。
并行复制不是魔法开关,它只负责把“可并行”的事务真正跑起来。如果主库事务本身设计就高度耦合、锁粒度粗、提交节奏混乱,从库再开 32 个 worker 也救不了锁竞争。

















