sql server 删除大数据量必须用 delete top (n) + order by 循环,否则可能漏删或重复删;order by 和 where 字段需有索引,top 建议 ≤5000 以控制日志压力。

SQL Server 必须用 DELETE TOP (n) + ORDER BY 循环
不加 ORDER BY 的 TOP 是危险的:SQL Server 每次可能选不同行,导致漏删或重复删;SET ROWCOUNT 已弃用,子查询分页会全表扫描。实测 TOP (5000) 是安全上限,超过 10000 行单次日志压力陡增。
-
ORDER BY字段必须是索引列(最好是主键),否则排序开销压垮 I/O -
WHERE条件字段也必须有索引,否则每次循环都在全表扫描 - 示例结构:
WHILE 1=1 BEGIN DELETE TOP (5000) FROM logs WHERE create_time
MySQL 分段删必须带 ORDER BY + LIMIT,且避免 OFFSET
没索引支撑的 ORDER BY 会触发 filesort,IO 直接拉满;LIMIT 5000 OFFSET 10000 偏移越大越慢,还容易因并发插入跳过数据。正确姿势是锁定扫描顺序,用子查询兜底。
- 确保
WHERE和ORDER BY字段有联合索引,例如INDEX idx_status_id (status, id) - 别写
DELETE FROM t WHERE status = 0 LIMIT 5000—— 缺少ORDER BY就不稳定 - 推荐写法:
DELETE FROM t WHERE id IN ( SELECT id FROM ( SELECT id FROM t WHERE status = 0 ORDER BY id LIMIT 5000 ) AS tmp );
PostgreSQL 更稳的做法是用主键区间切片而非 LIMIT
DELETE ... LIMIT 在高并发下行为不稳定:新插入的行可能挤进下一批范围,导致漏删;同时它不改变 MVCC 扫描路径,仍可能触发全表膨胀。主键区间更可控,且便于暂停/重试。
- 先查边界:
SELECT MIN(id), MAX(id) FROM logs WHERE create_time - 按步长分段删,例如:
DELETE FROM logs WHERE id BETWEEN 100001 AND 105000 - 步长建议 1000–5000;超过 10000 容易让单次事务 undo 占用过高
- 删完立刻执行
ANALYZE logs,否则后续查询可能因统计信息陈旧而选错执行计划
所有数据库都绕不开的三个硬约束
再标准的分批逻辑,撞上这三条也会失败,而且报错往往不直接指向它们。
- 外键约束未禁用:
ALTER TABLE child NOCHECK CONSTRAINT fk_name必须提前执行,否则每批都校验外键,性能断崖下跌 - 自动统计更新开着:
ALTER DATABASE your_db SET AUTO_UPDATE_STATISTICS OFF,删一半时触发统计更新,锁表+扫全表 - 主从延迟高:如果在主库跑分批删,从库可能因单批执行太久而延迟飙升,甚至复制中断;需监控 replication lag 并动态调小
TOP或LIMIT值
真正难的不是写出循环语句,而是判断哪张表该用 3000 行一批,哪张得压到 1000 行——这取决于索引深度、行宽、日志备份频率和当前系统负载。











