必须分批删除千万级数据,否则日志暴涨、锁表、主从延迟;需用DELETE TOP(n)配ORDER BY、WAITFOR DELAY和@@ROWCOUNT终止,并检查恢复模式、禁用统计更新与触发器。
直接结论:别用单条 DELETE 清千万级数据,必须分批提交,否则日志暴涨、锁表、主从延迟全来。
DELETE 会让日志文件爆炸SQL Server 在 DELETE 每一行时,都要在事务日志里记下完整前像(用于回滚)+ 事务元数据。删 500 万行,就写 500 万条日志记录——而且整个操作包在一个事务里,日志无法截断,直到事务结束。即使开了 FULL 恢复模式并配了日志备份,只要这个大事务没提交,备份也截不断它占着的日志空间。
9002 错误(日志已满)、LOG_BACKUP 备份失败、DBCC SQLPERF(logspace) 显示日志使用率长期 99%WHERE 条件且命中索引,也无法规避单事务日志累积问题DELETE TOP (n) 循环必须带这三样东西只写 DELETE TOP (5000) FROM t WHERE ... 是半吊子方案,容易翻车。
ORDER BY(比如 ORDER BY id),否则重复删或漏删——TOP 不保证稳定顺序WAITFOR DELAY '00:00:00.1'(100ms),给检查点(Checkpoint)和日志截断留出窗口IF @@ROWCOUNT = 0 BREAK 判断终止,别用 EXISTS(SELECT 1 FROM ...) 查剩余,后者可能触发全表/全索引扫描默认配置会拖慢分批删除节奏,甚至让日志增长更隐蔽。
FULL 恢复模式?查:SELECT name, recovery_model_desc FROM sys.databases WHERE name = 'YourDB';如果是,确保日志备份任务在运行中,否则分批也没用ALTER DATABASE YourDB SET AUTO_UPDATE_STATISTICS OFF,删完再开——否则每批删完触发统计更新,扫表+写日志双加重负DISABLE TRIGGER),删完再启,避免额外日志和锁竞争不是越大越好,也不是越小越稳,得看你的 I/O 和锁等待表现。
TOP (5000)~TOP (10000):太小(如 100)导致事务开销占比过高,CPU 和日志写入频次反升;太大(如 50000)单次日志压力仍明显,且锁持有时间拉长sys.dm_exec_requests 中的 wait_type:如果频繁出现 LCK_M_U 或 WRITELOG,说明锁或日志写入成瓶颈,需调小批次或加 WAITFOR 延长tempdb 压力:若语句涉及排序(如 ORDER BY 非索引列),大量 sort 操作会挤占 tempdb 空间,间接拖慢日志写入真正卡住人的,往往不是“怎么写循环”,而是忘了关统计、忘了查恢复模式、或者把 WAITFOR 写成秒级延迟还埋怨主从不同步——这些细节不处理,分批也白分。