SPID阻塞同服务器其他数据库进程且查询未跨库如何溯源排查
单库SELECT引发跨库阻塞的溯源排查步骤
这类问题90%以上的误区是:你看到的SELECT根本不是持有锁的语句,只是阻塞会话当前正在执行的最后一句,锁是会话之前的操作遗留的。按以下顺序排查,基本能定位根因:
- 先拉取阻塞头SPID的全量锁持有信息
不要依赖活动监视器、sp_who2这类工具显示的当前执行语句,直接查系统DMV拿到锁的真实分布,执行以下T-SQL,把参数替换为你查到的阻塞头会话ID:
SELECT locks.request_session_id, DB_NAME(locks.resource_database_id) AS 锁所属数据库, locks.resource_type, locks.request_mode AS 锁模式, locks.resource_associated_entity_id, sess.login_name, sess.host_name, sess.program_name, text.text AS 会话最近执行语句 FROM sys.dm_tran_locks locks JOIN sys.dm_exec_sessions sess ON locks.request_session_id = sess.session_id CROSS APPLY sys.dm_exec_sql_text(sess.sql_handle) text WHERE locks.request_session_id = @阻塞头SPID AND locks.request_status = 'GRANT' -- 只看已经授予的持有的锁,不看等待中的锁
重点核对锁所属数据库字段:如果确实有其他业务库的锁,说明锁根本不是当前这句SELECT申请的;如果锁全在tempdb或者master/msdb这类系统库,属于实例级共享资源阻塞,会被误判为跨业务库阻塞。
- 排查未提交事务泄漏(最高发原因)
这是连接池场景下最常见的问题:- 执行
DBCC OPENTRAN查看对应业务库下的活跃事务,确认阻塞头SPID是否持有一个开启时间远长于当前SELECT执行时间的活跃事务。 - 核查应用侧事务逻辑:是否存在异常分支漏写
COMMIT/ROLLBACK、是否全局开启了隐式事务(SET IMPLICIT_TRANSACTIONS ON)、是否在事务内执行过跨库操作后没等事务提交就把连接还给了连接池。
这类场景下,连接被连接池复用后执行你看到的单库SELECT时,之前事务申请的所有锁(包括其他库、tempdb的锁)都不会释放,自然会阻塞全实例对应资源的访问,表面看起来就是这句SELECT在阻塞其他库的进程。
- 执行
- 排查实例级共享资源阻塞
如果锁都落在实例级共享资源上,本质不是跨库锁,是共享资源堵了影响所有库:- 如果锁所属数据库是tempdb:检查问题SELECT的执行计划,是否存在缺失索引导致的大面积扫描、排序/哈希连接溢出到tempdb、使用了大量临时表/表变量,这类操作会在tempdb上持锁,而所有数据库的查询都可能用到tempdb,阻塞时就会表现为跨库影响。
- 如果锁类型是SERVER、ENDPOINT、METADATA这类实例级资源:检查SELECT是否查询了实例级系统视图、是否触发了实例级元数据锁、是否因为内存不足触发了实例级资源等待。
- 排查跨库访问隐式链路
- 检查实例是否开启了跨数据库所有权链(
cross db ownership chaining),如果开启,哪怕你写的SELECT只查当前库对象,如果对象的所有者和其他库对象所有者一致,查询时可能隐式访问其他库的元数据、权限,意外持有其他库的锁。 - 检查当前SELECT的执行计划,是否隐式引用了其他库的对象、链接服务器对象,比如视图、同义词、触发器底层关联了其他库的表,你从语句表面看不出来。
- 检查实例是否开启了跨数据库所有权链(
- 排查锁升级异常
检查问题SELECT的返回行数、持锁数量,如果单次查询申请的行锁/页锁数量超过阈值(默认5000个)会触发锁升级,如果会话之前持有跨库的事务锁,锁升级过程中可能出现锁范围异常扩散,导致跨库阻塞。
排查核心原则:永远以数据库端记录的实际锁归属、事务状态为准,不要相信应用侧打印的执行SQL——连接复用场景下,你看到的SQL和持锁的SQL大概率不是同一句。
内容的提问来源于stack exchange,提问作者Aviwe
相关产品推荐
相关产品推荐

