如何不重启SQL Server终止KILLED/ROLLBACK状态的xp_cmdshell
SQL Server xp_cmdshell僵死在KILLED/ROLLBACK状态的非重启修复方案
前置校验
先执行以下T-SQL确认会话属于僵死状态而非正常回滚:
SELECT session_id, status, command, wait_type, wait_time, tempdb_allocations, percent_complete, blocking_session_id FROM sys.dm_exec_requests WHERE session_id = <问题会话SPID>
判定标准:
wait_time持续增长、tempdb_allocations持续上涨、percent_complete超过10分钟无变化,才属于需要干预的僵死状态,否则优先等待系统自主完成回滚。
方案1:KILL命令强制触发回滚(最低风险)
首次执行KILL后会话卡住时,先查询回滚进度,确认无进度后重复执行KILL指令触发中断:
-- 查询回滚进度 KILL <问题会话SPID> WITH STATUSONLY; -- 进度无变化时重复执行强制中断 KILL <问题会话SPID>;
xp_cmdshell属于外部调用存储过程,僵死多为关联的Windows进程成为孤儿进程,SQL调度器丢失进程句柄导致,该方案成功率约30%,无效时直接走后续方案。
方案2:终止关联孤儿Windows进程(中风险,无业务中断)
xp_cmdshell调用的外部进程全部运行在SQL Server本机,而非发起连接的源服务器,之前找不到对应PID是查找位置错误,按以下步骤操作:
- 先在SQL中查询SQL服务进程PID:
SELECT SERVERPROPERTY('PROCESSID') as sqlservr_pid - 登录SQL Server所在的Windows服务器,管理员身份打开命令提示符,执行以下命令列出所有由SQL服务派生的cmd进程:
wmic process where "name='cmd.exe' and parentprocessid=<上一步查到的sqlservr_pid>" get processid,commandline - 从返回结果中找到命令行参数匹配僵死xp_cmdshell执行内容的进程ID,执行强制终止:
taskkill /PID <匹配到的cmd进程PID> /F
操作完成后等待1分钟左右,僵死会话会自动回收,tempdb占用会逐步释放。
方案3:回收SQL内部僵死工作线程(中高风险,无实例重启)
如果找不到对应cmd进程,说明进程句柄已完全泄漏,可通过终止对应工作线程回收会话:
- 查询僵死会话对应的工作线程地址:
SELECT th.worker_address FROM sys.dm_exec_requests r JOIN sys.dm_os_workers w ON r.task_address = w.task_address JOIN sys.dm_os_threads th ON w.worker_address = th.worker_address WHERE r.session_id = <问题会话SPID> - 执行以下命令触发线程异常回收:
DBCC STACKDUMP('<上一步查到的worker_address值>');
执行后等待2-3分钟,SQL异常处理机制会自动回收僵死线程,会话状态会自动清除。
兜底方案(无实例重启,会中断所有运行中xp_cmdshell任务)
以上方案全部无效时,可通过卸载重载xp_cmdshell模块强制回收所有关联会话:
EXEC sp_configure 'show advanced options',1; RECONFIGURE; -- 临时关闭xp_cmdshell EXEC sp_configure 'xp_cmdshell',0; RECONFIGURE; -- 等待1分钟后卸载模块 DBCC xp_cmdshell(FREE); -- 按需重新开启xp_cmdshell EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE; EXEC sp_configure 'show advanced options',0; RECONFIGURE;
操作前确认业务侧无正在运行的xp_cmdshell任务,避免正常任务被中断。
内容的提问来源于stack exchange,提问作者Krishna508
相关产品推荐
相关产品推荐

