直接delete大表数据会锁表、日志爆满、主从延迟飙升;应分批按主键范围删除,避免limit,并执行analyze table/update statistics及外键检查。

直接 DELETE FROM table WHERE 条件?别这么干
会锁表、日志爆满、主从延迟飙升,甚至触发 Lock wait timeout exceeded 或被 DBA 杀掉。哪怕加了索引,单次删超 10 万行也极大概率出问题。根本原因不是语句本身,而是 InnoDB/SQL Server 对每行都记完整 undo/redo 日志 + 逐行加锁,事务越大越危险。
MySQL 分批删:用主键范围,别用 LIMIT
LIMIT 在高并发写入场景下可能跳过或重复删——因为新插入的行 ID 可能落在当前批次区间内。更稳的方式是按主键切片:
- 确保 WHERE 条件字段(如
created_at)有索引,且最好和主键组成联合索引(如(created_at, id)) - 每次取一段连续
id范围,例如:DELETE FROM orders WHERE id BETWEEN 100000 AND 104999 AND created_at - 每批控制在 5000 行以内,执行后显式
COMMIT,再DO SLEEP(0.1)缓冲 IO 和复制压力 - 用
SELECT MIN(id), MAX(id)先定位待删数据整体范围,避免循环中反复全表扫描
SQL Server 优先用 DELETE TOP + WHILE 循环
SQL Server 不支持 LIMIT,但 TOP 是安全选择:
- 写法示例:
DELETE TOP (5000) FROM His_Order WHERE CreateDate - 必须配合
WHILE循环和IF @@rowcount 判断退出条件 - 不要在简单恢复模式下长期运行——
ALTER DATABASE SET RECOVERY SIMPLE会影响日志链连续性,只应在明确接受 RPO 风险时临时启用 - 如果要删的数据占原表 70% 以上,直接重建表更快:
SELECT * INTO orders_new FROM orders WHERE CreateDate >= '2023-01-01',再原子切换
删完不是结束:碎片和统计信息必须处理
删除大量数据后,InnoDB 表的 DATA_FREE 值可能很高,但 OPTIMIZE TABLE 会锁表;SQL Server 的索引页空闲空间也不会自动回收。更关键的是:
-
ANALYZE TABLE(MySQL)或UPDATE STATISTICS(SQL Server)必须执行,否则后续查询可能走错执行计划 - 检查是否残留外键依赖——比如删了主表旧订单,子表
order_items的对应记录是否同步清理?漏掉就会变成孤儿数据 - 归档日志或 binlog 位点要核对,抽样比对删前删后 count,不能只信
ROW_COUNT()











