根本原因是mysql 8.0默认的commit_order依赖追踪模式仅按提交顺序判断事务依赖,无法识别行级冲突,导致高并发单表写入时所有事务被强制串行回放;必须将主库binlog_transaction_dependency_tracking设为writeset、binlog_row_image设为full,从库slave_parallel_type设为logical_clock,并确保表有主键或唯一键,否则并行复制形同虚设。

为什么Seconds_Behind_Master一直涨,哪怕开了slave_parallel_workers
根本原因不是“没开并行”,而是并行策略没生效。MySQL 8.0 默认的 binlog_transaction_dependency_tracking 是 COMMIT_ORDER,它只靠事务提交顺序推导依赖关系,对高并发单表写入几乎无效——所有事务被串行排队,slave_parallel_workers 形同虚设。
常见错误现象:SHOW SLAVE STATUS\G 中 Seconds_Behind_Master 持续上涨,Slave_SQL_Running_State 卡在 Reading event from the relay log 或 Waiting for dependent transaction to commit,说明 SQL 线程实际仍是单线程回放。
- 必须把主库
binlog_transaction_dependency_tracking改为WRITESET(动态可设:SET GLOBAL binlog_transaction_dependency_tracking = 'WRITESET';) - 主库
binlog_row_image必须为FULL(否则无法提取写集,WRITESET退化失效) - 从库
slave_parallel_type要设为LOGICAL_CLOCK(注意:名字有误导,但 MySQL 8.0 的LOGICAL_CLOCK模式才真正支持WRITESET调度) -
slave_parallel_workers建议设为 CPU 核数的 1.5–2 倍(如 16 核设 24),但需配合performance_schema.replication_applier_status_by_worker观察空闲率,避免过度争抢
WRITESET 并行在什么场景下会失效
WRITESET 不是银弹,它的并行能力完全取决于事务修改的行是否冲突。只要两个事务更新同一主键或唯一键,就会被判定为依赖,强制串行。
典型失效场景:
- 高频更新同一张小表的单个配置行(如
UPDATE config SET value = ? WHERE key = 'site_status') - 批量导入时按主键顺序写入(相邻事务天然竞争同一行)
- 业务未定义主键或唯一键,导致
binlog_row_image = FULL也无法生成准确写集 - 大事务内包含多条修改同一行的语句(整个事务被当作一个原子单元阻塞后续)
验证是否真正在用 WRITESET:查 performance_schema.replication_applier_status_by_worker,若多个 worker 的 LAST_SEEN_TRANSACTION 不同,且 LAST_APPLIED_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP 与 LAST_APPLIED_TRANSACTION_IMMEDIATE_COMMIT_TIMESTAMP 有明显差异,说明并行调度已启动。
从库硬件和参数配置不匹配,再好的并行也白搭
并行复制把压力从单线程 SQL 线程分散到多个 worker,但每个 worker 都要读 relay log、解析 event、执行 SQL、刷 redo —— 这些操作全依赖 CPU、内存带宽和磁盘 I/O。如果从库硬件弱于主库,或者关键参数压得太紧,worker 会排队等资源,反而加剧延迟。
- 从库
innodb_buffer_pool_size至少为主库的 70%–80%,否则大量页要从磁盘读取,worker 频繁等待 I/O - 从库磁盘必须是 NVMe SSD;SATA SSD 或 HDD 在高并发回放下 IOPS 上不去,
slave_parallel_workers开再多也卡在iowait -
slave_pending_jobs_size_max建议设为 128M–256M(默认 128M),太小会导致 worker 频繁等待 job 队列,太大则可能 OOM -
slave_preserve_commit_order = ON必须开启(尤其搭配WRITESET),否则事务提交顺序错乱,引发数据不一致
大事务和无主键表是WRITESET的硬伤
WRITESET 依赖事务修改的主键/唯一键哈希值做冲突判断。没有主键或唯一键的表,MySQL 无法确定哪几行被改,只能退化为全表扫描级依赖,整个事务被锁死,后续所有事务都得等它。
- 检查表结构:
SELECT TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = 'your_db' AND CONSTRAINT_NAME = 'PRIMARY'; - 对缺失主键的业务表,必须补上自增主键或业务唯一键;DDL 操作务必用
pt-online-schema-change,避免复制中断 - 拆分大事务:比如
DELETE FROM logs WHERE ts ,改成 <code>LIMIT 10000循环删,每次事务只影响少量行,WRITESET才能真正并行 - 避免在主库执行
ALTER TABLE,DDL 会生成元数据锁,阻塞所有后续事务写入 binlog,从库只能干等
最常被忽略的一点:主库开了 WRITESET,但从库没重启 mysqld(或没重载配置),slave_parallel_type 仍为旧值;或者主库 binlog_row_image 被人为改成 MINIMAL,导致写集为空——这些细节一漏,整个并行链路就断在第一环。











