必须加order by主键的limit删除,否则高并发下易漏删或重复删;where条件字段需有索引,避免filesort和全表扫描;批量大小宜为500–5000行,配合sleep降低负载;主键范围删除适用于自增或时间递增主键场景。

直接 DELETE 海量数据几乎必然导致锁表、日志暴涨、主从延迟,这不是“慢不慢”的问题,而是“能不能用”的问题。真正能落地的方案只有三类:分批删、换表删、删索引再删。
为什么 LIMIT 分批删除必须加 ORDER BY
只写 DELETE FROM t WHERE condition LIMIT 1000 很危险——MySQL 可能每次选不同行,重复删或漏删;更糟的是,若没索引支撑,会全表扫描,每批都扫一遍。
- 必须搭配
ORDER BY primary_key_column(如id),确保每次从“已处理位置”往后推进,避免重复或跳过 - WHERE 条件字段(如
create_time)必须有索引,否则ORDER BY+LIMIT仍会触发 filesort 和全表扫描 - 批量大小别贪大:
LIMIT 500比LIMIT 10000更稳,尤其在高并发写入场景下,锁行时间短、冲突少
主键范围删除适合什么场景
当你要删的数据在主键上是连续段(比如删掉所有 id 的旧记录),用范围切片比 <code>LIMIT 更高效,因为不用排序、不依赖索引命中率。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
- 先查出边界:
SELECT MIN(id), MAX(id) FROM big_table WHERE create_time - 再循环删:
DELETE FROM big_table WHERE id BETWEEN ? AND ? AND create_time - 注意:如果表有大量碎片或主键不连续(比如被频繁 delete/insert 过),
BETWEEN可能误删或漏删,务必先EXPLAIN验证执行计划
删 90% 以上数据时,别碰 DELETE
删掉绝大部分数据(比如 1.6 亿删剩 250 万),DELETE 不是慢,是根本不可控——事务日志撑爆磁盘、锁表数小时、主从延迟不可逆。
- 正确做法是建新表:
CREATE TABLE t_new LIKE t,然后INSERT INTO t_new SELECT * FROM t WHERE keep_condition - 关键一步:用
RENAME TABLE t TO t_old, t_new TO t原子切换,业务无感知 - 如果原表有外键或触发器,
LIKE不会复制它们,得手动补;分区表还要显式重建分区规则
多索引大表删除前先 DROP 索引
每删一行,InnoDB 就要更新所有索引。一个 5 个索引的表,删 100 万行 ≈ 更新 500 万索引项,IO 和 CPU 成倍上涨。
- 临时删掉非主键索引:
DROP INDEX idx_name ON big_table(主键索引不能删) - 删完再重建:
CREATE INDEX idx_name ON big_table (col) - 实测:某 3000 万行带 4 个二级索引的表,删 200 万行耗时从 8 小时降到 15 分钟
最易被忽略的一点:无论用哪种方案,操作前必须确认 binlog_format 是 ROW(不是 STATEMENT),否则从库重放可能失败;同时检查 innodb_log_file_size 是否足够,避免删到一半 redo log 写满崩溃。










