在SQLServer运行环境中,未提交的事务若长期滞留,会持有锁并占用日志空间,导致其他会话阻塞、tempdb压力上升以及日志文件无法截断。要定位这些事务,核心思路是借助系统动态管理视图(DMV)将“会话”与“事务”关联,并按事务启动时间计算持续时间。

一、理解SQLServer事务与DMV的关系
SQLServer中的每个事务都会被分配一个事务ID,并在sys.dm_tran_active_transactions中记录其状态、开始时间和类型。而会话(连接)与事务的映射关系保存在sys.dm_tran_session_transactions里,它告诉我们某个session_id当前绑定了哪些transaction_id。最后,sys.dm_exec_sessions提供会话的登录名、主机名、最近请求时间等上下文信息。
单纯查询活动事务视图只能看到事务自身,无法知道是谁开启的;单纯看会话视图又看不到未提交事务的细节。因此必须把三张视图通过transaction_id和session_id做连接,才能完整刻画“哪个连接、在什么时间、开启了什么类型的事务且一直没提交”。这种关联方式是后续所有查找脚本的基础。
二、使用DMV查找长时间未提交事务
下面脚本以“超过60分钟”为例,筛选出仍未提交且持续时间过长的会话事务。你可以按需调整变量@great_than_minutes。
DECLARE @great_than_minutes INT;
SET @great_than_minutes = 60;
SELECT
s.session_id AS [会话ID],
s.login_name AS [登录名],
s.host_name AS [主机名],
s.program_name AS [程序名],
t.transaction_id AS [事务ID],
at.name AS [事务名称],
at.transaction_begin_time AS [事务开始时间],
DATEDIFF(MINUTE, at.transaction_begin_time, GETDATE()) AS [持续分钟],
CASE at.transaction_type
WHEN 1 THEN '读/写'
WHEN 2 THEN '只读'
WHEN 3 THEN '系统'
ELSE '未知'
END AS [事务类型],
CASE at.transaction_state
WHEN 0 THEN '未初始化'
WHEN 1 THEN '已初始化未开始'
WHEN 2 THEN '活动'
WHEN 3 THEN '已结束'
WHEN 4 THEN '已提交'
WHEN 5 THEN '已回滚'
ELSE '其他'
END AS [事务状态]
FROM sys.dm_tran_session_transactions t
INNER JOIN sys.dm_exec_sessions s
ON t.session_id = s.session_id
INNER JOIN sys.dm_tran_active_transactions at
ON t.transaction_id = at.transaction_id
WHERE at.transaction_begin_time < DATEADD(MINUTE, -@great_than_minutes, GETDATE())
AND at.transaction_state = 2
ORDER BY at.transaction_begin_time ASC;
该查询返回的每一行都代表一个“活跃且超时”的未提交事务。通过[持续分钟]列可直观判断严重程度;[登录名]与[程序名]则帮助定位责任应用。若发现某事务已持续数小时,基本可判定为代码遗漏commit或异常未回滚。
需要注意的是,sys.dm_tran_active_transactions中的transaction_begin_time在分布式或嵌套事务场景下可能并非绝对的业务开启时刻,但在绝大多数单机OLTP系统中,它足以作为发现长事务的依据。另外,系统内部事务(transaction_type=3)通常无需干预。
三、进一步分析阻塞与锁占用
找到长事务后,往往还需确认它阻塞了谁。可结合sys.dm_exec_requests与sys.dm_tran_locks查看其持有锁模式及被阻塞会话。
SELECT
r.session_id AS [被阻塞会话],
r.blocking_session_id AS [阻塞源会话],
r.wait_type AS [等待类型],
r.wait_time AS [等待毫秒],
l.resource_type AS [锁资源类型],
l.request_mode AS [锁模式]
FROM sys.dm_exec_requests r
LEFT JOIN sys.dm_tran_locks l
ON r.session_id = l.request_session_id
WHERE r.blocking_session_id <> 0
AND r.blocking_session_id IN (
SELECT session_id FROM sys.dm_tran_session_transactions
);
上述脚本列出当前正被长事务会话阻塞的请求,以及它们等待的锁资源类别。如果长事务持有OBJECT或KEY类型的排他锁(X),影响面通常较大。此时应评估是在业务低峰期终止会话,还是推动应用修复。
在生产环境,建议将第一段查找脚本配置为定时作业,超过阈值即报警。这样无需人工巡检,也能在事务拖垮性能前介入。
四、处理长时间未提交事务的方法
确认某会话确实为僵死事务且无法由应用自行提交后,可使用KILL命令结束该会话,SQLServer会对其事务做回滚释放锁与日志。
-- 将 75 替换为实际查到的会话ID KILL 75;
KILL会引发事务回滚,若事务很大,回滚可能耗时较久,期间系统仍可能有一定压力,但远比任其长期占用要好。更根本的做法是与开发团队核对代码:检查是否使用了隐式事务(SET IMPLICIT_TRANSACTIONS ON)、是否在catch块中遗漏ROLLBACK、或是否在连接池回收前未提交。
此外,开启SET XACT_ABORT ON能让语句错误时自动终止并回滚整个事务,减少“半开不提交”的概率。对关键批处理,还应加上事务超时控制,避免无限等待。
五、预防与监控建议
除了被动查找,建立长效机制更重要。可在监控平台定时采集DMV数据,绘制事务持续时间趋势图;对超过5分钟未提交的事务发黄牌,超过30分钟红牌并通知负责人。
| 阈值 | 动作 |
|---|---|
| 5分钟 | 记录日志并标记观察 |
| 30分钟 | 发送告警给DBA与应用负责人 |
| 60分钟 | 评估后执行KILL并复盘代码 |
通过视图查询加流程规范,能显著降低长事务引发故障的概率。本质上,查找只是手段,让事务“短平快”才是目标。
SQLServer未提交事务sys.dm_tran_session_transactions修改时间:2026-08-04 03:30:13