大事务会导致binlog传输和从库回放完全冻结而非变慢;因其溢出binlog_cache_size并独占组提交队列,且未拆分时MTS也无法并行执行,必须按主键BETWEEN切片拆分+调整slave_parallel_type、workers等参数协同优化。

大事务会直接卡死 binlog 传输和从库回放,不是“慢”,是“停”——必须拆分事务,再配对调整主从参数,否则调任何单点参数都无效。
为什么大事务会让 binlog 传输和 SQL 回放同时冻结?
主库提交一个改 300 万行的 UPDATE,binlog_cache_size 默认仅 32KB,很快溢出到磁盘临时文件;它又独占 binlog_group_commit 队列,其他事务全在 Writing to binlog 状态排队。从库 IO Thread 虽能持续接收,但 SQL Thread(或 MTS worker)必须等这个巨量 binlog event 完整写入 relay log、解析、执行完,才更新 Relay_Log_Pos 和 Seconds_Behind_Master。这期间所有后续 binlog 都堆在 relay log 文件里不动,Relay_Log_Space 暴涨就是铁证。
哪怕开了 slave_parallel_workers=8,只要事务本身没拆,它仍被当作一个原子单元塞进单个 worker,根本不会触发并行调度。
安全拆分 UPDATE/DELETE 的唯一可靠方式
别碰 LIMIT + ORDER BY created_at,高并发下排序结果漂移、漏数据、重复更新全是必然结果。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 锚定主键或唯一索引字段(如
id),用BETWEEN ? AND ?切片,每次处理固定步长(建议 1k–5k 行) - 每批独立
START TRANSACTION; ... COMMIT;,禁用autocommit - 执行后查
ROW_COUNT():为 0 就立即退出,避免空跑 - 记录最后成功处理的
id(写入临时表或 Redis),下次从id + 1开始继续 - 每批加
timeout和最多 2 次重试,失败记日志并退出,不阻塞后续批次
拆完必须同步调的主从关键参数
只拆事务不调参,小事务照样串行回放,延迟照旧。
-
slave_parallel_type = LOGICAL_CLOCK(MySQL 5.7.22+)或COMMIT_ORDER(8.0+),不能用DATABASE -
slave_parallel_workers = 4~8,设太高反而增加调度开销 -
slave_preserve_commit_order = ON,否则事务依赖关系被破坏,数据可能不一致 - 主库确认启用组提交:
binlog_group_commit_sync_delay = 100000~200000(单位微秒) - 主库调大
binlog_cache_size(如 20MB),避免频繁落盘;检查binlog_cache_disk_use是否为 0
最容易被忽略的隐形陷阱
拆分脚本本身没有事务原子性——某批 UPDATE 失败时,不能靠回滚恢复,只能靠断点续跑逻辑兜底。而断点值若只存在内存或本地文件,从库重启或部署变更就丢失。生产环境必须把最后处理的 id 写进一张带主键的持久化表,或外部存储(如 Redis),且每次写入前加校验防止覆盖。










