直接结论:单条DELETE操作覆盖几十万行且WHERE字段无索引时,从库SQL线程会因每行全表扫描而锁死,导致Seconds_Behind_Master突增至几千秒且监控失真;根本原因是ROW格式下从库需对每行before_image做全表匹配,100万行即100万次全表扫描,复制链路冻结,延迟真实反映在Slave_SQL_Running_State而非Seconds_Behind_Master。

直接结论:别让单条 DELETE 操作覆盖几十万行,尤其不能在没索引的字段上删;否则从库 SQL 线程会被锁死,Seconds_Behind_Master 会突然跳到几千秒,且监控可能失真。
为什么无索引的 DELETE 会让从库“卡住”而不是“慢一点”?
MySQL 主从复制(ROW 格式)中,从库不是执行原始 SQL,而是根据 binlog 里的每行 before_image 去本地表里“找人再删”。没主键、没 WHERE 字段索引时,它只能全表扫描——每删一行,都要扫一遍整张表。100 万行?就是 100 万次全表扫描。
-
SHOW PROCESSLIST里从库 SQL 线程长期显示Deleting或end,不是卡在网络或 IO,是卡在 CPU 密集型匹配上 - 即使主库
DELETE只花 2 秒提交,从库可能要 300 秒才能回放完,期间整个复制链路冻结 -
Seconds_Behind_Master可能卡在 0 或旧值不动,真正反映延迟的是SQL_Remaining_Delay(如果启用了延迟复制)或Slave_SQL_Running_State字段
分批 DELETE 必须满足的三个硬性条件
用 DELETE ... LIMIT 不等于解决问题——错用反而更糟。
- 禁止
OFFSET:如DELETE FROM t ORDER BY id LIMIT 1000 OFFSET 10000,每次扫描都从头开始,越往后越慢,且并发写入时范围漂移导致漏删/重删 - 必须基于有索引的单调字段分段:例如
WHERE id BETWEEN 10000 AND 20000,确保每批范围不重叠、全覆盖、可预测;执行前先EXPLAIN验证是否走索引 - 每批后显式
COMMIT,并建议加SLEEP(0.05)(非必需但实测有效),避免瞬时 I/O 和锁争用打满从库资源
比“怎么删更快”更重要的事:绕开 SQL 线程
真正缓解延迟的关键,不是优化 DELETE 本身,而是不让它垄断 SQL 线程。
- 对日志类、归档类大表,优先用
TRUNCATE TABLE或DROP PARTITION(MySQL 8.0+ 支持):它们是 DDL,不走 SQL 线程,不写 undo,秒级生效 - 确认
slave_parallel_type = LOGICAL_CLOCK且slave_parallel_workers >= 8:单线程回放是底层瓶颈,不调这个,拆再细也没用 - 紧急时可临时在从库设
SET SESSION sql_log_bin = OFF,手动执行删操作(仅限已停 SQL 线程且确认无 GTID 的场景)
最容易被忽略的点:延迟不是“从库追不上”,而是“从库被一条语句钉死了”。检查 Slave_SQL_Running_State 比盯 Seconds_Behind_Master 更早发现问题;而加索引、分批逻辑、并行参数这三件事,必须同时做,缺一不可。

















