我正在运行一个多线程操作,该操作使用 SqlConnection
和 Parallel.ForEach()
从 SQL Server 数据库中获取数据,并且发生了以下情况:
- 我将
SqlConnection
包装在using
语句中,以便正确处理连接。 - 在成功运行一段时间后,我的进程始终抛出包含在
AggregateException
中的SqlException
(“建立与SQL Server..") - 我发现这发生在大约 2^14 (16384) 次数据库调用(所有线程中总共)。
- 我启动了 perfmon,我可以看到这也是在抛出异常时打开的 TCP 连接数(“Connections Established”计数器)。
- 我确信我的代码中没有连接泄漏——我查询数据库的地方很少,它们都正确地处理了连接(事实上,我没有其他模式用于查询数据库比 Vanilla
using(...)
) - 我关闭了连接池并且发生了相同的行为。
- 奇怪的是,如果我在 SQL Server 中删除了一个使我的查询速度更快的索引,该操作会成功完成(尽管非常缓慢)- 不会抛出任何异常。我观察到建立的连接数线性上升到大约 13K,然后稳定了一段时间,然后有一些线性下降的时期,所有这些都是在操作运行的过程中进行的。
- 我的结论是,随着索引的建立,.net 处理数据的速度超过了它关闭连接的速度,并最终达到某种操作系统或 .NET 套接字的最大阈值。如果没有索引,.NET 仍然会维护过多的连接,但它有时间关闭足够多的连接,以免达到最大打开套接字阈值。
我不知道如何指示 .NET 关闭这些连接。我认为当 SqlConnection
被释放时,这会自动神奇地发生。
最佳答案
问题似乎与连接的打开方式有关。我正在使用这个构造函数
public SqlConnection(string connectionString, SqlCredential credential)
似乎 ADO 创建了一个新的连接池,即使 connectionString
相同并且 credential
封装了相同的凭据。我的猜测是 ADO 无法以某种方式将凭据与先前的调用相关联(如果我通过引用相等性使用相同的凭据对象,它可能会起作用?在我的例子中,我在每次调用时都创建了一个新的 SqlCredential)。
由于每次都会创建一个新池,因此 TCP 连接数会激增。我认为这在关闭连接池时也有意义。可能一些套接字或 TCP 设置在这里启动(保持事件状态?)并且操作系统保持连接打开以遵守这些设置。
关于.net - ADO.net 关闭 TCP 连接的速度不够快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33106463/