sys.dm_exec_requests 和 sys.dm_exec_sessions 可查正在运行的 delete 事务,需关注 command='delete'、status='running/suspended' 及 wait_type(如 lck_m_x、writelog),配合 sys.dm_exec_sql_text 获取具体语句;kill 后状态转为 rollback,回滚不可逆且耗时,不可重复 kill。

怎么查出正在跑的 DELETE 事务
先别急着 KILL,得确认它真在跑、且没卡死在别的地方。SQL Server 不像 MySQL 那样直接看 SHOW PROCESSLIST,得查动态管理视图:sys.dm_exec_requests 和 sys.dm_exec_sessions。
重点字段是 command(必须为 DELETE)、status(得是 running 或 suspended),还要看 wait_type——如果长期是 LCK_M_X、WRITELOG 或 PAGEIOLATCH_*,基本就是锁住/日志刷不过来/磁盘慢了。
推荐一句速查语句:
SELECT r.session_id, r.status, r.command, r.wait_type, r.wait_time,
s.login_name, s.host_name, t.text
FROM sys.dm_exec_requests r
JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.command = 'DELETE' AND r.status IN ('running', 'suspended');
KILL spid 后 DELETE 真的停了吗
KILL 命令只是向会话发中断信号,不是立即拔电源。SQL Server 会在下一个检查点或事务边界响应,所以你可能看到 status 变成 rollback,而不是瞬间消失。
这时候别反复 KILL,否则可能触发死锁检测器误判,反而让系统更卡。真正耗时的是回滚(rollback)阶段:SQL Server 要把已修改的页逐条还原,尤其是大事务,undo 日志量可能远超原始 DELETE 数据量。
常见误区:
-
KILL后立刻去查表数据,发现“好像没少”——那是因为回滚还没完,不是没删 - 以为
KILL就等于撤销,其实它只终止执行,不保证数据恢复到原状 - 对正在
ROLLBACK的会话再KILL一次,可能导致数据库进入可疑状态(suspect)
为什么有时 KILL 不起作用,还卡在 rollback
根本原因就一个:事务太大,undo 操作本身成了瓶颈。SQL Server 回滚是单线程串行执行的,不能并行加速,且要重读所有被改过的数据页、重建索引项、释放锁、清理版本链(如果开了 RCSI)。
影响回滚速度的关键因素:
- DELETE 影响的行数越多,回滚越慢(不是线性,是指数级增长)
- 表上有大量非聚集索引——每删一行,每个索引都要 undo 一次
- 数据库开启了
READ_COMMITTED_SNAPSHOT(RCSI)——版本存储在tempdb,如果tempdb磁盘慢或空间不足,rollback 会卡在写 version store -
recovery interval设置过长,导致 checkpoint 不及时,redo log 积压太多
比 KILL 更可控的提前防御手段
事后 KILL 是下策。生产环境该做的是事前控制:
- 所有大 DELETE 必须加
TOP (n)分批,比如DELETE TOP (5000) FROM orders WHERE ...,配合WHILE @@ROWCOUNT > 0循环 - 避免在高峰时段跑全表 DELETE;用
SET LOCK_TIMEOUT 5000设定锁等待上限,超时就退出,不硬扛 - 确认
autocommit关闭(SSMS 默认关,但很多 ORM 或脚本默认开),否则一执行就提交,ROLLBACK无效 - 提前在测试库跑一遍相同条件的 DELETE,用
SET STATISTICS IO ON看逻辑读和扫描行数,预估影响范围
最常被忽略的一点:KILL 之后,那个 spid 对应的事务日志空间不会立刻释放,log_reuse_wait_desc 可能显示 ACTIVE_TRANSACTION,直到 rollback 完成。这意味着日志文件可能持续膨胀,甚至填满磁盘——别只盯着会话,也得盯 DBCC SQLPERF(LOGSPACE)。










