【发布时间】:2020-05-15 13:17:01
【问题描述】:
我有一个程序在线程池上运行的任务中加载大量数据(每次迭代约 800K-1M 行)(请参阅下面的违规代码示例);同时运行的任务不超过 4 个。这是程序中唯一与该数据库建立连接的地方。在我的笔记本电脑(和其他同事相同的笔记本电脑)上运行该程序时,该程序运行良好。但是,我们可以通过远程桌面访问另一个工作站,它比我们的笔记本电脑更强大。该程序在其列表中失败了大约 1/3 到 1/2。所有任务都返回异常。
第一个例外是:“超时已过期。在从池中获取连接之前已经过了超时期限。这可能是因为所有池连接都在使用中,并且已达到最大池大小。”我尝试过在 StackOverflow 上搜索、搜索、搜索,并将头撞在桌子上,试图弄清楚这是怎么回事。一次运行的任务不超过 4 个,任何时候都不应有超过 4 个连接。
为此,我尝试了两件事:(1) 我在 conn.Open() 行周围添加了一个 try/catch,如果出现 InvalidOperationException 会清除池——这似乎有效 [没有让它一路运行,但大大超过了以前的水平],但以性能为代价。 (2) 我将 ConnectionTimeout 更改为 30 秒而不是 15 秒,这不起作用(但让它继续进行一点)。我也曾经尝试过 ConnectRetryInterval=4(错误地选择了这个而不是 ConnectRetryCount)——这导致了另一个错误“最大请求数是 4,800”,这很奇怪,因为我们仍然不应该接近 4,800请求或连接。
简而言之,我很茫然,因为我无法弄清楚是什么导致了这种连接泄漏,只有在更高速度的计算机上。我也无法让该计算机上的 Visual Studio 直接调试 - 任何人可能对在哪里尝试解决此问题有任何想法将不胜感激。
(跟进c# TaskFactory ContinueWhenAll unexpectedly running before all tasks complete)
private void LoadData()
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = "redacted";
builder.UserID = "redacted";
builder.Password = "redacted";
builder.InitialCatalog = "redacted";
builder.ConnectTimeout = 30;
using (SqlConnection conn = new SqlConnection(builder.ConnectionString))
{
//try
//{
// conn.Open();
//} catch (InvalidOperationException)
//{
// SqlConnection.ClearPool(conn);
// conn.Open();
//}
conn.Open();
string monthnum = _monthsdict.First((x) => x.Month == _month).MonthNum;
string yearnum = _monthsdict.First((x) => x.Month == _month).YearNum;
string nextmonthnum = _monthsdict[Array.IndexOf(_monthsdict, _monthsdict.First((x) => x.Month == _month))+1].MonthNum;
string nextyearnum = _monthsdict[Array.IndexOf(_monthsdict, _monthsdict.First((x) => x.Month == _month)) + 1].YearNum;
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = @"redacted";
cmd.Parameters.AddWithValue("@redacted", redacted);
cmd.Parameters.AddWithValue("@redacted", redacted);
cmd.Parameters.AddWithValue("@redacted", redacted);
cmd.CommandTimeout = 180;
SqlDataReader reader = cmd.ExecuteReader();
while(reader.Read())
{
Data data = new Data();
int col1 = reader.GetOrdinal("col1");
int col2 = reader.GetOrdinal("col2");
int col3 = reader.GetOrdinal("col3");
int col4 = reader.GetOrdinal("col4");
data.redacted = redacted;
data.redacted = redacted;
data.redacted = redacted;
data.redacted = redacted;
data.redacted = redacted;
data.Calculate();
_data.Add(data); //not a mistake, referring to another class variable
}
reader.Close();
cmd.Dispose();
conn.Close();
conn.Dispose();
}
}
【问题讨论】:
-
另外,这是带有 WinForms UI 的 .NET 4.6.2
-
老实说,我不确定我是否可以考虑一些环境因素(例如,查询中使用的主表超过 20 billion 行,其中有几个指数,并且该程序在更多消费者常见的硬件上运行良好)。我还将更新这个问题,以包括我的另一个问题中的按钮单击过程,这样它们就在一个地方,因为这两个形成了 MRE 代码。然后,我将简要更新一下,为任何希望测试的人提供建议的数据库架构和字段名称。
-
这不是一个答案,但是:
SqlCommand和SqlReader也实现了IDisposable模式 - 所以你应该这样做using(var con...) {using(var cmd...){using var reader...){}}}这样,你不需要关闭或处置其中任何一个明确地,using语句正在为您执行此操作。另外,你不应该使用AddWithValue,你应该为你的参数使用唯一的名字。
标签: c# ado.net connection-pooling sqlconnection