SQL Server 2019中无表查询为何成为阻塞主查询?
排查建议:看似无害的查询引发SQL Server阻塞的原因分析
一、会话资源持有异常
- 未终止的残留事务:即使当前执行的是
SELECT SUSER_NAME(), SUSER_ID();这类无表访问的查询,会话可能之前执行过修改类操作(如INSERT/UPDATE/DELETE)但未提交或回滚事务,导致锁资源一直被持有。此时该会话会成为阻塞源头,杀掉进程后事务自动回滚,锁释放。可通过sys.dm_tran_session_transactions查询阻塞会话是否存在活跃事务,用DBCC INPUTBUFFER(<阻塞PID>)查看会话历史执行语句。 - 客户端结果未取完:像查询报表的
SELECT AbsoluteName FROM REPORT WHERE ReportName = 'namehere',看似已执行完成,但如果客户端程序未完整读取结果集,SQL Server会话会一直处于等待状态(常见等待类型ASYNC_NETWORK_IO),持续占用会话资源并引发阻塞。可通过sys.dm_exec_requests查看阻塞会话的wait_type确认。
二、环境与配置差异
- 隔离级别或快照设置不同:该客户数据库可能启用了特殊隔离级别(如
SERIALIZABLE),或未开启READ_COMMITTED_SNAPSHOT,导致即使简单查询也会触发更长时间的锁持有或范围锁。对比其他正常客户的sys.databases中is_read_committed_snapshot_on等参数,以及会话级的SET TRANSACTION ISOLATION LEVEL配置。 - 服务器资源瓶颈:客户服务器存在CPU、内存或磁盘IO瓶颈时,SQL Server处理会话的速度变慢,即使简单查询也会因资源争抢导致排队阻塞。比如内存不足引发频繁页交换、磁盘IO过高导致锁释放延迟,可通过
sys.dm_os_performance_counters监控CPU使用率、页交换率、磁盘读写等待指标。
三、应用程序层面问题
- 连接池资源泄漏:应用的连接池配置错误,或代码中未正确释放数据库连接,导致会话长期处于打开状态,残留的事务或资源未被清理。比如连接被复用前未完成事务提交/回滚,后续请求被该会话阻塞。
- 隐式事务未关闭:应用可能开启了
SET IMPLICIT_TRANSACTIONS ON,执行完查询后未显式提交事务,导致会话一直持有事务资源。可通过sys.dm_exec_sessions的is_implicit_transaction_on字段检查阻塞会话的隐式事务状态。
四、SQL Server内部异常
- 补丁或版本差异:该客户的SQL Server 2019可能未安装最新累积更新,存在会话资源泄漏的已知bug,导致会话无法正常释放资源。对比其他正常客户的补丁版本,尝试安装对应最新补丁验证。
- 扩展组件残留:如果阻塞会话之前调用过扩展存储过程、CLR程序或第三方插件,可能存在资源未释放的情况,后续执行简单查询时仍持有资源引发阻塞。可通过
sys.dm_exec_sessions的last_executed_procedure字段查看会话历史执行的组件。
即时排查操作
当阻塞发生时,立即执行以下SQL收集关键信息:
-- 查看完整阻塞链及会话状态 SELECT r.blocking_session_id, r.session_id, r.wait_type, r.wait_time, s.transaction_isolation_level, s.is_implicit_transaction_on, COALESCE(qt.text, '无SQL文本') AS last_executed_sql FROM sys.dm_exec_requests r JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) qt WHERE r.blocking_session_id <> 0 UNION ALL SELECT 0 AS blocking_session_id, s.session_id, s.wait_type, s.wait_time, s.transaction_isolation_level, s.is_implicit_transaction_on, COALESCE(qt.text, '无SQL文本') AS last_executed_sql FROM sys.dm_exec_sessions s OUTER APPLY sys.dm_exec_sql_text(s.sql_handle) qt WHERE s.session_id IN (SELECT blocking_session_id FROM sys.dm_exec_requests WHERE blocking_session_id <> 0); -- 检查阻塞会话的活跃事务 SELECT s.session_id, t.transaction_id, t.transaction_begin_time, CASE t.transaction_state WHEN 0 THEN '未初始化' WHEN 1 THEN '活跃' WHEN 2 THEN '已提交' WHEN 3 THEN '正在回滚' WHEN 4 THEN '已回滚' END AS transaction_state FROM sys.dm_tran_session_transactions s JOIN sys.dm_tran_transactions t ON s.transaction_id = t.transaction_id WHERE s.session_id IN (SELECT blocking_session_id FROM sys.dm_exec_requests WHERE blocking_session_id <> 0);
内容的提问来源于stack exchange,提问作者PG Tuser
相关产品推荐
相关产品推荐

