必须启用WRITESET并满足GTID+ROW+log_slave_updates=ON三要素,否则大表UPDATE因无主键/唯一键或非唯一条件导致WriteSet失效,自动降级为单线程回放。

MySQL 8.0 中仅设 slave_parallel_workers 无法解决大表更新延迟——必须启用 WRITESET 并满足 GTID + ROW + log_slave_updates=ON 三要素,否则仍走单线程回放。
为什么大表 UPDATE 仍卡在单线程?
大表更新(如 UPDATE t_user SET status=1 WHERE create_time )会产生大量行变更事件,但若并行复制未真正激活,所有这些事件仍由一个 SQL 线程串行处理。常见误判是看到 <code>slave_parallel_workers=16 就以为生效了,实际 SHOW PROCESSLIST 只显示一个 SQL Thread,Seconds_Behind_Master 持续上涨。
根本原因不是参数没写对,而是以下任一缺失:
-
binlog_format不是ROW(MIXED或STATEMENT下writeset完全不生成) -
gtid_mode=OFF或enforce_gtid_consistency=OFF(WRITESET依赖 GTID 唯一标识事务) -
log_slave_updates=OFF(从库 relay log 解析阶段无法构建 writeset 依赖图,slave_preserve_commit_order失效) -
slave_parallel_type误设为DATABASE(8.0 中该模式已过时,且对单库大表无效)
主库必须配的 WRITESET 相关参数
主库不配,从库收不到 writeset 信息,再怎么调从库参数都白搭。这些设置需在主库执行并写入 my.cnf 永久生效:
-
binlog_transaction_dependency_tracking=WRITESET(核心开关,缺它整个机制瘫痪) -
transaction_write_set_extraction=XXHASH64(哈希算法,比默认XXHASH32冲突率更低) -
binlog_transaction_dependency_history_size=25000(控制 writeset 历史缓存大小,避免频繁清空影响冲突判断) -
binlog_format=ROW且binlog_row_image=MINIMAL(减少 binlog 体积,加快网络传输和解析) -
gtid_mode=ON+enforce_gtid_consistency=ON(强制 GTID 模式,不可妥协)
执行后需重启主库或至少执行 FLUSH BINARY LOGS 让新配置对后续事务生效。
从库关键参数与验证要点
从库参数本身不复杂,但验证是否真并行比配置更重要:
-
slave_parallel_type=LOGICAL_CLOCK(注意:MySQL 8.0.22+ 才支持WRITESET类型,此前版本只能设此值,WRITESET是 tracking 方式,不是 parallel_type) -
slave_parallel_workers=16(建议设为 CPU 核心数的 2–4 倍,但超过 32 收益递减) -
slave_preserve_commit_order=ON(保障无冲突事务可并行、有依赖事务严格保序) -
log_slave_updates=ON(再次强调:这是从库端启用 writeset 的隐性前提)
验证是否生效的命令:
SHOW SLAVE STATUS\G
重点看:Slave_SQL_Running_State 是否出现多个 Worker thread;Seconds_Behind_Master 是否稳定在 0–1 秒内;执行 SELECT * FROM performance_schema.replication_applier_status_by_worker; 查看各 worker 是否有活跃任务。
大表更新场景下的额外注意点
即使并行复制启用成功,大表更新仍可能拖慢整体同步,因为单个事务内部无法拆分:
- 避免在主库直接跑全表
UPDATE或DELETE,改用分批(WHERE id BETWEEN ? AND ?)+ 限流(SLEEP(0.1)) - 确保大表有主键或唯一键——
WRITESET依赖这些键计算哈希,无主键则退化为事务级串行 - 从库
innodb_buffer_pool_size必须足够大,否则每批更新都触发磁盘刷脏,worker 线程集体卡在 IO 上 - 不要忽略
relay_log_recovery=ON,防止从库异常重启后 relay log 损坏导致并行中断
最易被忽略的是:大事务本身仍是原子单位,writeset 只能帮你在「不同事务之间」并行,无法加速「一个事务内部的百万行更新」——这需要业务层拆解,不是数据库参数能解决的。


















