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

slave_parallel_type=LOGICAL_CLOCK 依赖的 last_committed 是怎么失真的
根本原因不是从库“乱执行”,而是主库 binlog 中的 last_committed 值本身不可靠。这个值由主库在写入 binlog 时打上,表示“该事务提交时,上一个被所有线程共同看到的已提交事务的 sequence_number”。但它只在 binlog_order_commits = ON(默认)时才严格按提交顺序生成;一旦被设为 OFF,InnoDB 提交和 binlog 写入就解耦了——事务可能先完成引擎层提交,再异步刷 binlog,导致多个事务的 last_committed 被设成同一个值,哪怕它们逻辑上有先后依赖。
从库 SQL 线程组(worker)看到这些相同 last_committed 的事务,就会把它们放进同一并行组并发执行。如果其中两个事务更新同一张表的同一行(比如 A 更新 user.id=100 的余额,B 随后扣减该余额),从库就可能 B 先于 A 执行,直接报 ERROR 1032 或 ERROR 1062,然后触发重试机制。
-
slave_parallel_workers开得越多,冲突暴露越快——不是并行本身错,而是失真的分组放大了风险 - 用
mysqlpump --skip-binlog或某些 ORM 批量导入时绕过 binlog,会跳过last_committed标记,让后续事务的依赖链断裂 - 查看失真最直接的方式:
mysqlbinlog --verbose解析主库 binlog,检查相邻事务的last_committed和sequence_number是否出现“last_committed相同但sequence_number递增”这种异常模式
为什么会出现 “retried transaction 10 times in vain”
这不是重试失败,而是并行复制的依赖检测机制在“卡住”。当 worker 线程发现当前事务要更新某行,而该行正被另一个尚未完成的事务(属于不同 last_committed 组)修改时,它会主动暂停、等待那个事务 commit 完成——这叫“跨组等待”。但如果等待链形成环(A 等 B,B 等 C,C 又等 A),或某个事务迟迟不结束(比如大事务阻塞、锁等待超时),worker 就会在内部不断重试依赖判断,直到达到默认上限 10 次,抛出 Slave SQL thread retried transaction 10 times in vain。
- 该错误本质是逻辑依赖无法收敛,不是性能问题,调大
slave_transaction_retries只是掩盖症状 - 常见诱因:主库存在长时间运行的事务(如未提交的
BEGIN; UPDATE ...)、从库 relay log 中混入了跨库事务(如INSERT INTO db1.t1; UPDATE db2.t2),而slave_parallel_type = LOGICAL_CLOCK对跨库依赖识别能力弱 - 临时缓解可设
slave_preserve_commit_order = OFF,但这会放弃事务提交顺序保证,仅适用于明确接受最终一致性的读场景
slave_parallel_type = DATABASE 为什么也会卡住
看似简单按库名分发,实际极易退化为单线程。只要所有写操作都落在同一个库(比如全在 app_db),所有事务都会被路由到同一个 worker,其他 worker 彻底闲置。更隐蔽的问题是跨库事务:MySQL 遇到跨库语句(如 INSERT INTO db1.t1 SELECT * FROM db2.t2),会强制将整个事务放入“协调器线程”串行执行,并阻塞后续所有 DATABASE 组的调度,直到它完成。
- 即使你有 10 个库,只要其中 9 个只读、1 个承担全部写入,
slave_parallel_workers设成 16 也没用 -
SHOW PROCESSLIST中如果看到大量线程状态为Waiting for dependent transaction to commit,基本就是 DATABASE 模式下跨库事务引发的阻塞 - MySQL 8.0.22+ 的
WRITESET模式能缓解,但必须主库开启binlog_transaction_dependency_tracking = WRITESET,且从库仍需保持slave_parallel_type = LOGICAL_CLOCK——光改从库参数无效
修复前必须确认 relay log 顺序是否可信
别急着调参。先停掉从库复制:STOP SLAVE;,然后用 mysqlbinlog 逐条检查 relay log 文件中事务的物理顺序是否与主库 binlog 一致。如果 relay log 里已经出现事务 A 在 B 之后写入、但逻辑上 B 应先于 A 提交(比如 B 修改了 A 依赖的元数据),说明 IO 线程拉取过程就出错了——这种情况下强行开并行只会让不一致雪球越滚越大。
- 典型信号:
SHOW SLAVE STATUS\G中Relay_Log_File和Relay_Master_Log_File不一致,且Seconds_Behind_Master持续增长但Slave_SQL_Running_State卡在Reading event from the relay log - 若确认 relay log 顺序异常,不要尝试跳过错误,应重建从库:备份主库当前位点,重新
CHANGE MASTER TO,并确保主库sync_binlog = 1和innodb_flush_log_at_trx_commit = 1 - 上线前务必验证:
SELECT @@global.binlog_order_commits;必须为 1,否则并行复制永远踩坑
事务顺序问题从来不在从库“怎么跑”,而在主库“怎么记”。最容易被忽略的是:你改的从来不是从库参数,而是主库 binlog 的生成契约。


















