分批更新缓解主从卡顿的核心在于避免单次大事务产生海量binlog阻塞sql线程。通过limit分批(如每次5000行)并显式commit,可控制binlog量、释放锁、触发刷盘,使seconds_behind_master保持低位。

为什么分批更新能缓解主从卡顿
主从卡顿的根因不是“更新慢”,而是单次大事务产生海量 binlog、阻塞 SQL 线程回放、并引发从库 relay log 积压。分批更新把一个 200 万行的 UPDATE 拆成 2000 次各 1000 行的小事务,每次只生成少量 binlog,从库能快速消费,Seconds_Behind_Master 基本保持在个位数。
必须用 LIMIT 的分批写法(非主键范围)
适用于无连续主键、ID 稀疏或带逻辑条件(如 WHERE status = 'pending')的场景。错误写法是先 SELECT COUNT(*) 再循环——这会多一次全表扫描,且计数可能过期。正确姿势是依赖 ROW_COUNT() 判断是否结束:
SET @batch_size = 5000; REPEAT UPDATE orders SET status = 'processed' WHERE status = 'pending' LIMIT @batch_size; COMMIT; UNTIL ROW_COUNT() = 0 END REPEAT;
-
LIMIT必须出现在UPDATE语句末尾,MySQL 5.7+ 才支持 - 每次
COMMIT后立即释放锁,并触发 binlog 刷盘,避免堆积 - 不要在循环里加
SLEEP——它不减少 binlog 量,只拉长总耗时,反而加剧延迟
主键范围分批更稳定,但要防数据倾斜
当表有自增主键且分布均匀时,按 ID 分段最可控。但若业务中存在大量删除导致 ID 空洞,或新老数据混存(如 ID 1~10 万是冷数据,1000 万~1010 万是热数据),直接 BETWEEN @min_id AND @min_id + @batch_size - 1 会扫到大量无效行,效率反降。
- 先用
SELECT MIN(id), MAX(id) FROM orders WHERE status = 'pending'锁定有效区间 - 每批执行后,用
SELECT id FROM orders WHERE status = 'pending' ORDER BY id LIMIT 1 OFFSET @batch_size * n动态取下一批起点,比硬算@min_id + @batch_size更准 - 若
WHERE条件涉及非索引字段,务必先加索引,否则每批都在全表扫描
同步链路上的三个关键配置不能漏
光靠分批不够,主从两端参数不匹配,照样卡住。以下三项必须核对:
- 主库
binlog_format必须为ROW,否则并行复制失效,从库只能单线程回放 - 从库开启并行复制:
slave_parallel_type = LOGICAL_CLOCK且slave_parallel_workers = 4(建议设为 CPU 核数的 75%) - 从库关闭
innodb_flush_log_at_trx_commit = 1时,需同步调低sync_binlog,否则 relay log 写入快于磁盘刷盘,可能丢数据
最容易被忽略的是:分批更新脚本里没显式 COMMIT,或用了 AUTOCOMMIT=0 却忘了手动提交——这时每个批次实际还是一个长事务,锁和 binlog 堆积问题丝毫未解。











