根本原因是主库binlog_order_commits=OFF导致last_committed失真,使从库并行复制错误分组事务;修复需停复制、验证relay log顺序,必要时重建从库并确保主库binlog_order_commits=ON且sync_binlog=1、innodb_flush_log_at_trx_commit=1。

MySQL主从复制中事务顺序不一致,根本原因不是“乱序”,而是从库SQL线程单线程回放无法匹配主库的并发写入节奏——尤其当主库有多个事务在不同binlog position提交、但逻辑上存在依赖时,从库若按物理日志顺序串行执行,就可能违反事务间的因果关系。
为什么slave_parallel_workers开多了反而更易出错
MySQL 5.7+ 的并行复制默认基于 LOGICAL_CLOCK(即 slave_parallel_type = LOGICAL_CLOCK),它依赖主库 binlog 中的 last_committed 和 sequence_number 标记来分组可并行事务。但这个机制有个关键前提:主库必须启用 binlog_order_commits = ON(默认值),且不能被显式关闭。
如果主库设置了 binlog_order_commits = OFF,事务提交顺序和 binlog 写入顺序就可能错位,导致从库拿到的 last_committed 值失真,进而把本该串行的事务错误地划入同一并行组——最终出现更新丢失、主键冲突或 ERROR 1032 / 1062。
常见误操作包括:
- 为“提升主库性能”手动关掉
binlog_order_commits,却没意识到这对从库并行安全是破坏性的 - 使用
mysqlpump或某些 ORM 批量导入时开启--skip-binlog,绕过 binlog 顺序控制 - 主库启用了
group_replication但未同步调整 binlog 提交策略,与传统复制混用
slave_parallel_type = DATABASE 在什么场景下会失效
该模式只按库名(USE db_name)划分并行队列,看似简单,但实际极易退化为单线程:
- 所有写操作集中在同一个库(如
app_db),哪怕表很多,也只有一个 worker 处理 - 跨库事务(如
INSERT INTO db1.t1 ...; UPDATE db2.t2 ...)会被整个阻塞,直到前一个DATABASE组全部完成 - MySQL 8.0.22+ 虽支持
WRITESET,但要求主库开启binlog_transaction_dependency_tracking = WRITESET,且从库slave_parallel_type = LOGICAL_CLOCK才生效——不是光改从库参数就行
典型报错:Slave SQL thread retried transaction 10 times in vain,本质是依赖检测失败后反复重试,而非真正并行。
检查并行复制是否真正在工作
别只看 SHOW SLAVE STATUS\G 里 Slave_SQL_Running_State 是 “Reading event from the relay log” —— 这只是线程活着,不代表并行在跑。
真正要看的是:
-
SHOW PROCESSLIST中是否有多个Worker线程处于Executing状态(而不仅是Waiting for an event from Coordinator) -
SELECT * FROM performance_schema.replication_applier_status_by_worker,确认APPLYING_TRANSACTION列有多个非空值,且LAST_APPLIED_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP时间戳分散而非集中 - 监控
Seconds_Behind_Master是否随负载上升而明显回落——如果始终卡在高位,说明并行组划分失败或 worker 数量远超磁盘/IO承载能力
注意:slave_parallel_workers > 0 但 replication_applier_status_by_worker 里只有 coordinator 在动,基本等于并行没生效,大概率是主库 last_committed 全为 0 或全相同。
修复顺序不一致最直接的手段
当已发现数据错乱(比如从库多出/少了一行,或时间字段倒流),不要指望调参数“自动恢复”。事务顺序一旦错,靠重放无法自愈。
- 先停复制:
STOP SLAVE; - 确认当前
Relay_Log_File和Relay_Log_Pos,用mysqlbinlog --base64-output=DECODE-ROWS -v解析对应 relay log,定位最后几个事务的COMMIT顺序与主库是否对得上 - 若确认是并行导致的执行错乱(例如事务 T2 修改了 T1 刚插入的行,但 T2 先于 T1 执行),唯一可靠方式是:清空从库,用
mysqldump --single-transaction --master-data=2重建,并确保主库binlog_order_commits = ON且未被覆盖
容易被忽略的一点:即使你把 slave_parallel_workers 设为 16,只要主库 innodb_flush_log_at_trx_commit = 2 或 sync_binlog = 0,binlog 写入本身就不保序,从库再怎么并行也没意义——顺序问题的根子,往往在主库配置里。


















