优先使用sys.dm_exec_requests,因其为实时DMV、字段语义清晰(如blocking_session_id),而sys.sysprocesses是已废弃的兼容视图;sp_who通过blk列、sp_who2通过BlkBy列可直接识别阻塞源,但均不返回SQL文本,需配合DBCC INPUTBUFFER或dm_exec_sql_text获取。

查阻塞用 sys.sysprocesses 还是 sys.dm_exec_requests?
sys.sysprocesses 是兼容视图,SQL Server 2005+ 仍可查,但已废弃;
sys.dm_exec_requests 是动态管理视图(DMV),实时性更好、字段更明确,推荐优先使用。
sys.sysprocesses 中的
blocked 字段值为 0 表示未被阻塞,非 0 则表示该 SPID 正被该值对应的 SPID 阻塞;而
sys.dm_exec_requests 用
blocking_session_id 字段替代,语义更清晰。
注意:
sys.dm_exec_requests 不包含已断开连接但事务未提交的“幽灵会话”,这类会话可能只在
sys.sysprocesses 或
sys.dm_tran_locks 中暴露。
sp_who 和 sp_who2 能直接看出谁在阻塞吗?
sp_who 返回结果中
blk 列即阻塞源 SPID,值为 0 表示未被阻塞,非 0 即阻塞者 ID;
sp_who2 是扩展版,多出
BlkBy 列(等价于
blk),还带
CPU、
MemUsage、
Command 等实用字段。
两者都不返回 SQL 文本,要查具体语句必须配合
DBCC INPUTBUFFER(spid) 或
sys.dm_exec_sql_text(sql_handle)。
sp_who2 无官方文档支持,属未公开系统存储过程,生产环境慎用;
sp_who 官方支持,但仅限基础信息,不推荐用于深度诊断。
怎么一次性定位“头阻塞者”和它锁住的资源?
头阻塞者指
blocking_session_id = 0 且
status = 'suspended' 或
'running' 的活跃会话,同时自身没被别人堵。
常用组合查询:
- 先筛出所有被阻塞的请求:
SELECT session_id, blocking_session_id, wait_type, wait_resource FROM sys.dm_exec_requests WHERE blocking_session_id 0
- 再反查谁在阻塞它们:
SELECT session_id, status, command, sql_handle FROM sys.dm_exec_requests WHERE session_id IN (SELECT DISTINCT blocking_session_id FROM sys.dm_exec_requests WHERE blocking_session_id 0) AND blocking_session_id = 0
- 最后看它持有哪些锁:
SELECT resource_type, resource_description, request_mode, request_status FROM sys.dm_tran_locks WHERE request_session_id = @head_spid
wait_resource 值如
"KEY: 5:72057594044108800 (b52e3f0c6a0d)" 或
"OBJECT: 5:123456789" 可直接对应到
sys.dm_tran_locks 中的资源描述,这是判断锁粒度(行/页/表)的关键依据。
为什么 KILL 之后还会立刻再出现阻塞?
常见原因有三个:
- 应用层没正确关闭连接,事务未提交或回滚,KILL 后重连又执行同样逻辑;
- 查询本身缺乏索引,导致扫描全表或大量页,锁持有时间长,多个并发一上来就互等;
- 启用了锁升级(默认阈值 5000 行锁),一个 UPDATE 触发了表级 X 锁,后续所有 SELECT/INSERT 都被拦住。
验证锁升级是否发生:查
sys.dm_os_wait_stats 中
waiting_tasks_count 较高的
wait_type,如
LCK_M_SCH_M(架构修改锁)或持续出现的
LCK_M_X;再用扩展事件捕获
lock_escalation 事件。
别只盯着 KILL,得看
last_batch 时间和
open_tran 数——如果
open_tran > 1 且
last_batch 是几小时前,基本就是应用端漏了 COMMIT/ROLLBACK。