如何对 open_tran > 0 的旧 Sql 连接进行故障排除?
How to troubleshoot old Sql connections with open_tran > 0?
我们有一个 ASP.NET API 网站,它使用 NHibernate 连接到 SQL 服务器。
我们遇到的问题是,在一天中,与 SQL 服务器的连接数逐渐增加,并且有许多连接似乎没有返回到池中。我的意思是,如果我 运行 以下查询:
select * from master..sysprocesses s where datediff(minute, s.last_batch, getdate())>10
返回的行数不断攀升。 API 中的任何内容都不应花费 10 分钟才能完成。 小时 前有连接。
这是另一个线索:所有这些行的 open_tran
列的值为 1。所以在我看来,在 API 调用的某个地方,我们正在创建一个事务边界,并且该交易永远不会关闭。也许 DTC 可能会参与其中(我们有时会在一次调用中连接到多个数据库)。
问题是,我不知道如何进一步解决这个问题。我已经在 rogue spids 上尝试了 运行ning DBCC INPUTBUFFER
,但它们之间没有任何一致之处。
可能导致此行为的 anti-patterns/other 可能原因有哪些?
更新: 下面是创建数据库连接的方式。我们正在使用 StructureMap 进行依赖注入。我们在每个工作单元上创建两个数据库连接:一个 "normal" 用于常规 read/write 访问的连接,以及一个 运行 与 "ReadUncommitted" 的事务中的 "uncommitted" 连接] 访问(我们在读取大型 table 时遇到 table 锁定问题)。
这是来自 DI 注册表的代码:
For<ISession>().Transient().Use(context => context.GetInstance<ISessionFactory>().OpenSession());
For<ISessionUncommittedWrapper>().Transient().Use(context => new SessionUncommittedWrapper { Session = context.GetInstance<ISessionFactory>().OpenSession() });
然后,在工作单元中间件内部,我们创建一个 UnitOfWork
(当然有一个 using
块),它需要一个 ISession
和一个 ISessionUncommittedWrapper
在构造函数中。在 Begin()
方法中,我们有:
_uncommittedTransaction = SessionUncommittedWrapper.Session.BeginTransaction(IsolationLevel.ReadUncommitted);
在 UnitOfWork
的 Dispose()
方法中被处置(连同 ISession
和 ISessionUncommittedWrapper
)。
终于找到问题了
我发现问题的方法是创建一个日志记录 table,它跟踪 Session
的创建和处置,以及调用端点的 URI。通过查询所有未释放的连接,我发现在每个未释放连接的情况下,路径都以“/signalr”开头。
<facepalm>D'oh!</facepalm>
由于 OWIN 中间件主动创建 Sql 连接,因此它也在为 SignalR 这样做,从本质上讲,它使事务保持打开状态!因此,每个使用 SignalR 登录的客户端都在占用两个 Sql 连接。
我进行了适当的更改以从中间件中排除 SignalR 连接,现在我们不再有挂起的 Sql 连接。
我们有一个 ASP.NET API 网站,它使用 NHibernate 连接到 SQL 服务器。
我们遇到的问题是,在一天中,与 SQL 服务器的连接数逐渐增加,并且有许多连接似乎没有返回到池中。我的意思是,如果我 运行 以下查询:
select * from master..sysprocesses s where datediff(minute, s.last_batch, getdate())>10
返回的行数不断攀升。 API 中的任何内容都不应花费 10 分钟才能完成。 小时 前有连接。
这是另一个线索:所有这些行的 open_tran
列的值为 1。所以在我看来,在 API 调用的某个地方,我们正在创建一个事务边界,并且该交易永远不会关闭。也许 DTC 可能会参与其中(我们有时会在一次调用中连接到多个数据库)。
问题是,我不知道如何进一步解决这个问题。我已经在 rogue spids 上尝试了 运行ning DBCC INPUTBUFFER
,但它们之间没有任何一致之处。
可能导致此行为的 anti-patterns/other 可能原因有哪些?
更新: 下面是创建数据库连接的方式。我们正在使用 StructureMap 进行依赖注入。我们在每个工作单元上创建两个数据库连接:一个 "normal" 用于常规 read/write 访问的连接,以及一个 运行 与 "ReadUncommitted" 的事务中的 "uncommitted" 连接] 访问(我们在读取大型 table 时遇到 table 锁定问题)。
这是来自 DI 注册表的代码:
For<ISession>().Transient().Use(context => context.GetInstance<ISessionFactory>().OpenSession());
For<ISessionUncommittedWrapper>().Transient().Use(context => new SessionUncommittedWrapper { Session = context.GetInstance<ISessionFactory>().OpenSession() });
然后,在工作单元中间件内部,我们创建一个 UnitOfWork
(当然有一个 using
块),它需要一个 ISession
和一个 ISessionUncommittedWrapper
在构造函数中。在 Begin()
方法中,我们有:
_uncommittedTransaction = SessionUncommittedWrapper.Session.BeginTransaction(IsolationLevel.ReadUncommitted);
在 UnitOfWork
的 Dispose()
方法中被处置(连同 ISession
和 ISessionUncommittedWrapper
)。
终于找到问题了
我发现问题的方法是创建一个日志记录 table,它跟踪 Session
的创建和处置,以及调用端点的 URI。通过查询所有未释放的连接,我发现在每个未释放连接的情况下,路径都以“/signalr”开头。
<facepalm>D'oh!</facepalm>
由于 OWIN 中间件主动创建 Sql 连接,因此它也在为 SignalR 这样做,从本质上讲,它使事务保持打开状态!因此,每个使用 SignalR 登录的客户端都在占用两个 Sql 连接。
我进行了适当的更改以从中间件中排除 SignalR 连接,现在我们不再有挂起的 Sql 连接。