如何快速停止SQL Server中INSERT语句的执行及回滚进程
问题答复
两个诉求的可行性结论
- 无法中途停止回滚保留已插入记录。数据库事务的原子性要求单个事务要么全部提交生效,要么全部回滚撤销,不存在部分提交的中间状态。你取消查询触发的回滚是数据库引擎保障数据一致性的强制行为,哪怕你强制终止数据库进程、重启服务,实例重启后的崩溃恢复流程依然会先把这个未完成事务的所有修改全部回滚完毕,才会对外提供服务,不可能跳过回滚保留半插入的数据。
- 回滚阶段无法通过TRUNCATE操作终止进程。回滚会话会持有目标表、已插入数据行的排他锁,TRUNCATE需要获取表级架构修改锁,会被现有锁直接阻塞,直到回滚完全结束才会执行,完全达不到提前终止回滚的效果。
慢的核心原因
你对INSERT的结果量级估算完全错误。你写的
from xT1 A inner join xT1 B on a.i < b.i
是非等值自连接,返回的是C(n,2)=n*(n-1)/2行结果(n是xT1表的总行数):如果xT1有1000行,结果才是约50万行;如果xT1有1万行,结果就会达到5000万行;xT1到3万行时结果会冲到4.5亿行,远超过你预估的50万,这才是插入速度极慢的根本原因。
而回滚速度远慢于正向插入是SQL Server的正常表现:正向插入可以走批量日志优化,回滚是逐行执行逆操作,没有批量优化机制,10万条插入耗时2小时、回滚预估1天属于正常现象。
可选处理方案
- 零风险方案:等待回滚完成
不做任何额外操作,等回滚自然结束即可,整个过程不会产生数据一致性问题,回滚完成后再优化语句重新执行。 - 高风险应急方案(仅测试环境可用,生产严禁使用)
如果你在测试环境、完全不担心数据损坏,实在不想等待回滚,可以通过以下步骤强行跳过事务恢复:- 立即停止SQL Server服务
- 加跟踪标志
3608启动SQL Server实例,该参数会让实例仅启动master库,不执行用户库的恢复流程 - 将对应业务库切换为紧急模式,再切换为单用户模式,执行
DBCC REBUILD LOG命令重建库的事务日志 - 切换回多用户模式后,未完成的事务会被直接丢弃,不会执行回滚,你可以看到表中残留的部分插入数据。注意:这个操作大概率会导致数据页不一致、元数据损坏,操作完成后必须对全库做一致性校验、重建相关表,绝对不能在生产环境使用。
后续优化建议
等本次回滚完成后,重新执行插入时做以下调整:
- 先单独执行SELECT部分的语句,确认实际返回的结果量级,不要凭经验估算行数
- 改成分批插入逻辑,比如按a.i的取值范围分段,每插入一批就提交一次事务,就算中途取消也只会回滚当前小批次,不会出现超长回滚的问题
- 给xT1表的i字段创建主键/聚集索引,能大幅提升非等值自连接的匹配速度
内容的提问来源于stack exchange,提问作者asmgx
相关产品推荐
相关产品推荐

