DELETE大表让从库“卡死”而非“变慢”,因SQL线程必须串行执行完整事务,期间不更新Exec_Master_Log_Pos,导致Seconds_Behind_Master突跳至几千秒或卡在旧值;无主键时ROW格式binlog不含隐藏ROW_ID,从库被迫全表扫描。

DELETE大表为什么让从库“卡死”而不是“变慢”
因为从库SQL线程必须串行执行完整个DELETE事务,期间不更新Exec_Master_Log_Pos,所以Seconds_Behind_Master会突然跳到几千秒甚至卡在旧值不动——它不是“追得慢”,而是“根本没开始算延迟”。更隐蔽的是,ROW格式binlog里记录的是被删行的完整before_image,从库要靠这些字段去表里逐行匹配;没主键?那就只能全表扫描。
无主键表DELETE在从库触发全表扫描的底层原因
InnoDB即使没显式主键也会生成隐藏ROW_ID,但ROW模式binlog**不包含这个字段**。从库找不到高效定位依据,只能退化为TABLE SCAN或INDEX SCAN(取决于是否有可用二级索引)。千万级数据下,一次DELETE可能耗时数小时,而同样数据量的有主键表通常几分钟内完成。
- 表无主键 + 无二级索引 → 从库强制全表扫描(O(n)时间复杂度)
- 表无主键 + 有低选择性二级索引(如
status列只有0/1)→ 仍可能回表大量数据,效果接近全扫 - 主库用
WHERE id BETWEEN ? AND ?分批删,但从库没给id建索引 → 每批都全表扫,越删越慢
TRUNCATE能绕过延迟,但绝不是“万能解药”
TRUNCATE TABLE快,是因为它绕过了事务、undo日志和逐行binlog记录,直接重置段头、截断.ibd文件。但它有硬性前提:
- 表不能被其他表通过外键引用(否则报
ERROR 1701 (HY000)) - 需要
DROP权限,不只是DELETE权限 - 在GTID模式下,
TRUNCATE是DDL,会生成新GTID,需确保下游能正确处理 - MySQL 5.7及以下版本不支持
TRUNCATE TABLE t PARTITION (p1),误用会报语法错误
分批DELETE必须避开的三个实操陷阱
用LIMIT分页删看似安全,实际极易引发漏删、重复删、锁表加剧等问题。
- 禁用
OFFSET:如DELETE FROM t ORDER BY id LIMIT 1000 OFFSET 10000,每次都要从头扫描,10万行后单次就可能超秒级 - 必须用单调索引分段:例如
WHERE id >= 10000 AND id ,且<code>id字段必须有索引,否则分段失效 - 忽略从库I/O压力:每批
COMMIT后不加SLEEP(0.05),可能瞬间打满从库磁盘队列,反而拖慢整体回放速度
真正关键的不是“怎么删”,而是删之前确认从库执行计划是否走索引——用EXPLAIN SELECT * FROM t WHERE id BETWEEN 10000 AND 20000验证,没命中就加索引,别硬扛。











