不分批的批量删除必然导致数据库卡死或服务中断,因其引发锁持有时间延长、binlog/redo日志暴涨、缓冲池脏页积压及事务ID或undo段快速耗尽等三重压力叠加。

不分批的批量删除极大概率导致数据库卡死或服务中断——这不是性能问题,是锁、日志、内存三重压力叠加的必然结果。
MySQL单次DELETE为什么容易锁表和拖垮主从
InnoDB 对每行匹配记录加行锁,并同步写入 undo log 和 redo log。删几十万行时:
- 锁持有时间从毫秒级拉长到数秒甚至分钟,其他事务触发
Lock wait timeout exceeded - binlog 或 redo 日志暴涨,主库磁盘 IO 拉满,从库追不上,
Seconds_Behind_Master飙升 - InnoDB 缓冲池被大量脏页占满,频繁刷盘,进一步加剧 IO 压力
- 事务 ID(或回滚段)快速消耗,增加
undo log full或transaction_id wraparound风险
DELETE + LIMIT 不加 ORDER BY 就等于没分批
MySQL 的 DELETE ... LIMIT N 行为高度依赖执行计划。没 ORDER BY 时:
- 优化器可能放弃使用
LIMIT,转而全表扫描 + 逐行判断,LIMIT变成“最后才截断”,锁和日志压力照旧 - 高并发下两次
DELETE ... LIMIT 1000可能删到同一行(中间有新插入/更新),导致漏删或重复删 -
DELETE LOW_PRIORITY只是排队写请求,反而延长整体耗时,不解决锁和日志本质问题
正确写法必须带索引字段排序:DELETE FROM logs WHERE create_time ;<code>id 必须有索引(通常是主键),否则 ORDER BY 会触发 filesort,性能反降。
批次大小和节奏怎么控制才不翻车
批次不是越小越安全,也不是越大越快,关键在平衡事务开销与系统负载:
- 建议每批
LIMIT 1000–5000:小于 1000 增加解析/网络/事务提交开销;大于 5000 容易触发 DBA 长事务告警 - 每批后加
SLEEP(0.1)(应用层控制),避免 CPU 和 I/O 突增;高负载时可调至0.5 - 别用
OFFSET(如LIMIT 10000,1000),越往后偏移越慢,还可能跳过数据 - 监控
innodb_row_lock_time_avg(MySQL)或pg_locks行数(PG),若持续升高,说明批次仍太大
分批后磁盘空间没释放?这很正常
InnoDB 删除只是标记行“可复用”,不会立刻归还给操作系统。你看到 data_length 不变、df -h 占用照旧,不代表删失败了:
- 确认是否真在删:查
ROW_COUNT()或SELECT TABLE_ROWS FROM information_schema.TABLES观察趋势 - 真正回收空间需
OPTIMIZE TABLE或ALTER TABLE ENGINE=InnoDB,但这两者本身代价高,一般不在线执行 - 分区表可考虑
ALTER TABLE DROP PARTITION,前提是数据已按时间等维度做了分区
最容易被忽略的一点:分批策略是否有效,不能只看“删完了”,要看数据库有没有喘气的机会——每次删完立刻查 SHOW PROCESSLIST,确认没有 idle in transaction 或长时间 updating 状态。


















