ADO.net没有足够快地关闭TCP连接

时间:2015-10-13 15:19:05

标签: .net sql-server ado.net sqlconnection sqlexception

我正在运行一个多线程操作,使用SqlConnectionParallel.ForEach()从SQL Server数据库中获取数据,并且正在发生以下情况:

  • 我将SqlConnection包裹在using语句中,以便正确处理连接。
  • 我的进程在成功运行一段时间之后一直抛出SqlException包裹在AggregateException中(“在建立与SQL Server的连接时出现网络相关或特定于实例的错误..”)
  • 我发现这种情况恰好发生在对数据库的2 ^ 14(16384)次调用中(总共在所有线程中)。
  • 我启动了perfmon,我可以看到,这也是在抛出异常时打开的TCP连接数(“Connections Established”计数器)。
  • 我确信我的代码中没有连接泄漏 - 我查询数据库的地方很少,而且他们都正确地处理了连接(事实上,我没有其他模式用于查询数据库其他比香草using(...)
  • 我关闭了连接池,发生了同样的行为。
  • 奇怪的是,如果我在SQL Server中删除了一个快速查询的索引,则操作成功完成(尽管速度非常慢) - 不会抛出任何异常。我观察到,已建立的连接数线性上升到大约13K,然后稳定了一段时间,然后有一些时间呈线性下降,同时操作正在运行。
  • 我的结论是,随着索引的建立,.net处理数据的速度比关闭连接的速度更快,并最终达到某种操作系统或.NET套接字最大阈值。如果没有索引,.NET仍然会保持太多连接,但它有足够的时间关闭它们,以便不会达到最大打开套接字阈值。

我对如何指示.NET关闭这些连接感到茫然。我认为当SqlConnection被处置时,这种情况会自动发生。

2 个答案:

答案 0 :(得分:1)

问题似乎与连接的打开方式有关。我正在使用这个构造函数

public SqlConnection(string connectionString, SqlCredential credential)

似乎ADO创建了一个新的连接池,即使connectionString相同且credential封装了相同的凭据。我的猜测是,ADO无法将凭证与先前的调用联系起来(如果我通过使用引用相等来使用相同的凭证对象,它可能会起作用吗?在我的情况下,我会在每次调用时创建一个新的SqlCredential)。

由于每次都会创建一个新池,因此TCP连接的数量为气球​​。我认为这在连接池关闭时也是有意义的。可能一些套接字或TCP设置在这里开始(保持活着?),操作系统保持连接打开以尊重这些设置。

答案 1 :(得分:0)

即使连接对象被释放回池,在执行垃圾收集之前,非托管资源可能不会被释放。通过减慢命令(删除该索引),您可以给GC足够的时间将资源释放回操作系统。

如果您没有使用Async命令执行,则可以尝试在线程级别缓存连接对象。然后并行操作将继续重复使用相同的开放连接,而不是抓住新的套接字等。

我还没有针对防止某些资源过度使用的异步操作进行任何测试。我相信他们会使用IO完成端口来阻止阻塞。并且,您绝对不希望使用相同的连接对象在该状态下使用两个命令对象。