2015-10-13 56 views
3

我使用SqlConnectionParallel.ForEach()运行多线程操作,从SQL Server数据库获取数据和下面正在发生的TCP连接:ADO.net不打烊速度不够快

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

我不知道如何指示.NET关闭这些连接。我认为当SqlConnection被丢弃时,这种情况会自动发生。

+0

特别奇怪的是,当连接池使用默认设置(100个连接)处于活动状态时,该ADO可以打开更多的TCP连接 - 就好像池连接正在被抛弃一样?!?! – SFun28

回答

1

这个问题似乎与如何打开连接。我使用这个构造

public SqlConnection(string connectionString, SqlCredential credential)

看来,ADO创建一个新的连接池,即使connectionString是相同的,credential封装相同的凭据。我的猜测是,ADO以某种方式无法将凭据关联到之前的调用(如果我通过使用引用相等来使用相同的凭证对象,这可能会起作用?在我的情况下,我会在每次调用时创建一个新的SqlCredential)。

由于每次创建一个新的池,TCP连接的气球数量也会增加。我认为这在连接池关闭时也是有意义的。可能有一些套接字或TCP设置在此处插入(保持联络?),并且操作系统保持连接开放以尊重这些设置。

0

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

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

我还没有做过关于防止某些资源过度使用异步操作的任何测试。我相信他们会使用IO完成端口来防止阻塞。而且,你肯定不希望两个使用相同连接对象的命令对象处于该状态。

+1

也许定期调用GC.Collect()就足够了,我可以避免缓存的复杂性。 – SFun28

+0

调用'GC.Collect'后跟'GC.WaitForPendingFinalizers()'似乎没有帮助。 – SFun28