并行复制并非无锁并行,而是依据事务组(如不同库、表或writeset)分发执行;slave_parallel_workers无法绕过InnoDB行锁或MDL锁,一旦多线程操作同一行或表,便会因锁等待退化为串行。

并行复制不是“无锁并行”,而是按事务组分发执行
MySQL 的 slave_parallel_workers 并不能绕过 InnoDB 行锁或 MDL 锁——它只是把 Relay Log 中能并行的事务组(如不同库、不同表、不同 writeset)分发给多个 SQL Thread 执行。一旦多个并行线程操作同一张表、同一行,或触发隐式锁等待(比如 UPDATE ... WHERE 全表扫描),就会卡在锁上,实际变成串行。
常见错误现象包括:Seconds_Behind_Master 突然跳涨但 Slave_SQL_Running_State 显示 Updating 或 Waiting for table metadata lock;SHOW PROCESSLIST 里多个 SQL thread 长时间阻塞在相同表上。
- 启用并行复制前必须确认主库已开启
binlog_transaction_dependency_tracking=WRITESET(MySQL 5.7.22+),否则默认按库分发,跨库事务仍可能因锁冲突串行化 -
slave_parallel_type=LOGICAL_CLOCK在高并发写入下容易退化为单线程:当主库事务组提交时间戳重叠过多,从库无法区分依赖关系,会降级为顺序回放 - 即使启用了 writeset,若事务中包含 DDL(如
ALTER TABLE)、SELECT ... FOR UPDATE或跨表更新,writeset 无法准确计算冲突边界,导致并行调度失效
触发器和非确定性语句会让并行复制“自动降级”
并行复制依赖 binlog 中事务的可并行性标记(如 last_committed 和 sequence_number)。但 MySQL 在 ROW 格式下遇到非确定性操作时,会悄悄退回到 STATEMENT 记录方式,破坏 writeset 生成逻辑,使原本可并行的事务被强制串行执行。
典型触发场景包括:触发器中调用 NOW()、UUID()、USER(),或含子查询 + LIMIT、或触发器内更新了未显式出现在 binlog event 中的表。
- 验证方法:在主库执行
SHOW BINLOG EVENTS IN 'mysql-bin.0000xx' LIMIT 30,如果看到混有Query_log_event(STATEMENT)和Table_map_log_event(ROW),说明已发生隐式格式退化 - 这种退化不会报错,但会导致
slave_parallel_workers形同虚设——因为 STATEMENT 模式下事务间依赖关系不可推导,从库只能顺序执行 - 修复方向不是关掉触发器,而是重构逻辑:把 NOW() 替换为客户端传入的时间戳;把 UUID() 改为业务层生成并显式插入;避免在触发器里做跨表写入
从库 DDL 是并行复制的“硬中断点”
DDL 操作(尤其是 ALTER TABLE)在从库执行时会持有全局 MDL 锁,阻塞所有其他 SQL Thread,无论是否启用并行复制。这是 MySQL 复制层的硬限制,不是配置能绕过的。
你看到 Seconds_Behind_Master 在某个时间点后持续不归零,且 SHOW PROCESSLIST 显示大量线程状态为 Waiting for table metadata lock,基本就是 DDL 卡住了整个 SQL 回放队列。
- DDL 必须在主库执行,且应避开业务高峰;严禁在从库手动执行任何 DDL,哪怕加了
read_only=1也无法阻止 SQL Thread 自身触发的锁 - 大表 DDL 建议用
pt-online-schema-change工具,它通过影子表+触发器方式规避长锁,但要注意:该工具在主库执行时产生的大量小事务,反而可能加剧从库并行调度压力 - 监控项重点关注
INFORMATION_SCHEMA.INNODB_TRX中TRX_MYSQL_THREAD_ID对应的线程是否长时间持有TRX_STATE = 'LOCK WAIT',以及TRX_ROWS_LOCKED是否异常高
真正影响并行效率的,往往是主库的事务组织方式
从库并行能力上限由主库事务提交模式决定。如果主库写入是“一锅炖”式的大事务(比如单个事务更新 50 万行),那无论怎么调 slave_parallel_workers,从库都只能等它全部执行完才能调度下一个——writeset 无法拆分单个事务内部的行变更。
更隐蔽的问题是主库的组提交(group commit)配置不当:binlog_group_commit_sync_delay 设得过大,会导致多个事务被强行打包进同一个 binlog group,丧失独立调度机会。
- 单事务更新行数建议控制在 10 万以内;批量导入优先用
LOAD DATA INFILE+ 分批次COMMIT,而非拼大 SQL - 主库关键参数检查:
sync_binlog=1(保证 durability)、innodb_flush_log_at_trx_commit=1(避免日志丢失),但binlog_group_commit_sync_delay应设为 0 或极小值(如 1000 微秒),避免人为合并事务组 - 不要迷信
slave_parallel_workers=8就一定比=4快——如果主库事务粒度粗、writeset 冲突多,开更多 worker 反而增加线程调度开销和锁争抢概率


















