无任务状态的SQL进程阻塞其他进程问题成因咨询
成因分析
这类执行完简单查询仍持有锁的问题,核心原因几乎都是连接上存在未完成的事务,具体可以归为三类:
- 未提交的残留事务:连接此前执行的带
UPDLOCK的语句运行在显式/隐式事务中,后续执行的SELECT GETDATE()属于同一事务上下文,只要事务没有被显式提交或回滚,之前申请的更新锁就会一直被持有。如果外部调用方的代码缺失异常分支的事务处理逻辑,或者连接归还连接池时没有清理残留事务,就会触发该现象。 - 连接池泄漏:调用端逻辑异常导致已经废弃的数据库连接没有被正确关闭,SQL Server感知不到前端已经不再使用该连接,会一直维持连接上的事务和锁状态。
- 未处理完的MARS请求:如果调用端启用了多活动结果集(MARS),连接上存在未读取完成的结果集时,事务无法正常关闭,也会导致锁持续持有。
无损排查步骤
不需要干预外部公司的进程,在SQL Server侧执行以下查询即可验证猜想:
- 执行
DBCC OPENTRAN查询当前数据库运行时间最久的活动事务,记录事务对应的会话ID、启动时间,和阻塞发生的时间做匹配。 - 执行如下查询查看目标会话的事务状态、锁持有详情,确认是否存在未提交事务:
SELECT s.session_id, t.transaction_state, l.resource_type, l.request_mode, l.request_status, s.open_transaction_count FROM sys.dm_tran_session_transactions st JOIN sys.dm_tran_transactions t ON st.transaction_id = t.transaction_id JOIN sys.dm_tran_locks l ON st.transaction_id = l.request_owner_id JOIN sys.dm_exec_sessions s ON st.session_id = s.session_id WHERE st.session_id = <替换为你查到的阻塞会话ID>
解决方案
临时解除阻塞
如果确认是未提交事务导致,可在迁移任务的空闲窗口执行KILL <会话ID>终止会话,SQL Server会自动回滚该会话的残留事务,回滚完成后所有持有的锁都会释放。执行终止前可先运行KILL <会话ID> WITH STATUSONLY查看事务回滚预计耗时,评估对业务的影响。
长期规避方案
因为你的迁移场景可以接受读未提交的数据,可在迁移用的数据库连接字符串中新增ApplicationIntent=ReadOnly配置,同时在所有迁移查询执行前先运行SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED,或者在查询表时加WITH(NOLOCK)提示,让你的迁移查询不需要申请共享锁,也就不会被外部连接持有的更新锁阻塞,不影响迁移流程正常执行。
内容的提问来源于stack exchange,提问作者Stefan Lippeck
相关产品推荐
相关产品推荐

