如何在使用连接池时强制关闭SqlConnection物理连接?

42

我知道,如果实例化一个SqlConnection对象,我实际上是从连接池中获取了一个连接。当我调用Open()方法时,它将打开该连接。如果我在该SqlConnection对象上调用Close()或Dispose()方法,则会将其返回到连接池。

但是,这并没有告诉我它是否真的关闭了,还是我仍然拥有对数据库的活动连接。

我如何强制关闭网络级别的SqlConnection,或者至少知道何时它关闭?

示例:

using(SqlConnection conn = new SqlConnection(DBConnString)) {

   conn.Open();
   SqlCommand cmd = conn.CreateCommand();
   ...
   cmd.ExecuteReader(CommandBehavior.CloseConnection);
   ...
}
  • 第一次运行:300毫秒
  • 第二次运行:100毫秒
  • 第三次运行:100毫秒
  • 长时间等待(30分钟)后:300毫秒

如果连接真正关闭,第二和第三次运行也应该是300毫秒。但我知道对于这些运行,连接并没有真正关闭(我检查了SQL Server的活动监视器)。它不需要额外的200ms来执行身份验证/等操作。

我该如何强制连接真正关闭?

想法

  • CommandBehavior.CloseConnection是否有效?(显然不行?)
  • 在连接字符串中设置“Max Pool Size = 0”是否有效?(这将是一个无补偿的解决方案)
  • Dispose()是否有效?

参考资料


为什么连接真正关闭?性能低下? - Kiquenet
7个回答

56

1
我正在做这件事,但我的SQL连接仍然出现在sp_who2中,而我的SQL会话持有的AppLocks仍然被锁定(我希望它们随着连接的断开而消失)。我甚至设置了connStrBuilder.Polling = false - Jason Kleban

26

Moe Sisko的回答(调用SqlConnection.ClearPool)是正确的。

有时候你需要一条连接真正关闭而不是返回到池中。举个例子,我有一个单元测试会创建一个临时数据库,建立模式,测试一些东西,然后如果所有测试都通过,就删除临时数据库。

当连接池处于活动状态时,删除数据库命令会失败,因为仍然存在活动连接。从程序员的角度来看,所有的SQLConnections都已经关闭了,但由于池仍然保持一个连接处于打开状态,因此SQL Server不允许删除。

关于连接池是如何处理的最好的文档位于MSDN上的这个SQL Server连接池页面。我们不想完全关闭连接池,因为它可以提高重复打开和关闭的性能,但有时需要对SQLConnection进行"强制关闭",以便它释放数据库。

这可以通过ClearPool完成。如果在关闭/释放之前调用SqlConnection.ClearPool (connection),则在真正关闭/释放时它将被清除。


你描述的使用案例几乎和我所做的完全一样——测试某些数据库访问模式的性能,使用“冷”过程/数据缓存以及连接。 - Jeff Meatball Yang
您提供的链接已被添加到问题文本中。谢谢。 - Jeff Meatball Yang
我也是同样的情况 :-) - Cool Breeze
2
你可以通过在关闭连接之前输入 use master; 来解决 "当连接池处于活动状态时,由于仍然存在活动连接,删除数据库命令失败" 的问题。这不是回答 OP 的问题,但它可以解决你的问题。 - Ian Boyd

14

如果您不想使用连接池,您必须在SqlConnection.ConnectionString属性中指定它。例如:

"Data Source=MSSQL1;Database=AdventureWorks;Integrated Security=true;Pooling=false;"

释放或关闭SqlConnection对象只会关闭连接并将其返回到连接池。


2
通常情况下,您希望连接池完成其工作 - 您不希望连接真正关闭。
为什么您特别不希望连接返回到连接池中呢?

4
池中的连接会持有锁。 - Ian Boyd
2
我不确定你所指的锁是什么 - 一个已释放连接的底层连接应该不参与任何事务或持有任何与消费代码相关的锁。当然,它仍在使用网络端口和其他一些资源 - 但这些资源的获取成本正是为什么你不想释放它们的原因。除非你试图删除/恢复底层数据库或类似的根本性操作,否则这些锁并不会造成问题。 - Eamon Nerbonne
1
连接池中的一个连接可以防止对数据库的独占访问。 - kevmar

0

我看到你正在使用 .net,但由于这出现在谷歌查询中,让我给出一个 Java 的回应...

使用实现 Closeable() 接口的 DataSource,并在 DataSource 上调用 close 方法。Hikari 支持 Closeable。


-1

CommandBehavior.CloseConnection通常不建议使用,因为您无法确定连接是否已关闭。(我会尝试找到一些具体证据,我是从模糊的回忆中说出这句话的)。

Dispose()是最可靠的方法,因为它隐式调用Close()

@Alex演示的using结构只是另一种(程序员友好的)编写try-finally结构的方式,并添加了对象的隐式处理。

编辑:(问题编辑后)

我认为您对连接实际上是否关闭的担忧是没有必要的。连接将简单地返回到池中,以便可以轻松地重用它,而无需经过所有初始化。这并不意味着连接仍然与数据库保持活动连接。


3
池中的连接确实意味着它仍然与数据库保持活动连接。例如,您可以关闭一个连接对象,但由于该连接仍在池中,因此您仍将拥有分片数据库锁定(参见https://dev59.com/knRB5IYBdhLWcg3wxZ1K)。 - Ian Boyd

-2

Robert的SqlConnection.ClearPool(TheSqlConn)的回答完全符合我的要求。很高兴知道当必要时可以与池进行交互。

我的用例是:我们破坏了一个连接并让它返回到池中,如何检测它已经破坏并刷新它,以便下一个用户不会出现问题。

解决方案是:检测我们刚刚破坏了连接,并将其从池中清除,让池重新填充新的连接。

十年来我一直在编写SqlClient.SqlConnection,直到今天我才想到与池进行交互。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接