MySQL 5.7 MTS不自动解决事务冲突,因coordinator仅按LOGICAL_CLOCK或库名分发事务,不校验主键/唯一键等语义冲突,worker并发写同一行必报ERROR 1062;必须通过主键隔离、复合唯一键、全局ID等应用层方案提前规避,或在确认冗余后谨慎跳过。

MySQL 5.7 的多线程复制(MTS)本身不处理事务冲突——它只负责并发应用 binlog 事件,冲突必须由你提前规避或事后干预。所谓“解决冲突”,本质是防止冲突发生,或在冲突报错后安全恢复。
为什么 MTS 不会自动解决事务冲突
MTS 的 coordinator 线程只按 LOGICAL_CLOCK 或库名分发事务给 worker,不检查语义冲突(如主键重复、外键约束、唯一索引冲突)。一旦两个 worker 并发执行 INSERT INTO t(id) VALUES(1),第二个必然触发 ERROR 1062 并中断 SQL_THREAD。
- 这不是 bug,是设计使然:MySQL 复制层不维护跨事务的逻辑一致性校验
-
slave_preserve_commit_order=1只能保证“同一组提交事务”的回放顺序,无法阻止不同组事务写同一行 - 即使开启 GTID,
gtid_executed是 per-channel 维护的,不提供跨源/跨事务的冲突预判能力
避免冲突的核心手段:让事务天然不交集
真正有效的方案都在应用或架构层,而非复制参数:
- 按业务来源做主键隔离:美国主库写
id = 1000000 + origin_id,中国主库写id = 2000000 + origin_id - 用复合唯一键替代单列主键:比如把
(region, order_id)设为唯一键,并在所有 INSERT 中显式带上region - 禁用所有主库的
AUTO_INCREMENT,改由 Snowflake 或 UUIDv7 生成全局唯一 ID - 多源复制场景下,绝对不要让多个主库往同一张物理表写同构数据;改用分表(
order_us/order_cn)或视图聚合
遇到 ERROR 1062 后如何安全恢复
跳过错误不是解决,而是掩盖;必须确认跳过的确实是“可接受的冗余写入”:
- 先查具体冲突行:
SELECT * FROM t WHERE id = XXX;确认是否已存在、是否和主库一致 - 若确定是重复插入,且主库该行状态正确,可用:
SET GLOBAL sql_slave_skip_counter = 1; START SLAVE; - 更稳妥的做法是临时停 slave,手动补一条 UPDATE 或 DELETE,再
START SLAVE,避免用REPLACE INTO或INSERT ... ON DUPLICATE KEY UPDATE直接灌入从库——它们可能误删字段 - 长期依赖
slave_skip_errors配置等于放弃数据一致性审计,生产环境应禁用
容易被忽略的关键点
很多人调大 slave_parallel_workers 就以为万事大吉,但真正卡住 MTS 效能和引发隐性冲突的,往往是这些细节:
-
slave_pending_jobs_size_max默认 16M,若主库max_allowed_packet是 64M,worker 队列会频繁阻塞并打满日志(出现Coordinator has waited X times hitting slave_pending_jobs_size_max),必须设为 ≥ 主库该值 - DDL 语句永远单线程执行,且会强制同步所有 worker;一个大 DDL(如
ALTER TABLE)会让整个复制卡住数分钟 -
Exec_Master_Log_Pos在 MTS 下本就不精确,崩溃恢复时不能依赖它定位位点;必须结合performance_schema.replication_applier_status_by_worker查各 worker 的LAST_APPLIED_TRANSACTION和LAST_APPLIED_EVENT


















