如何解决ASP.NET和SQL Server之间的连接池问题?

时间:2009-03-22 09:50:47

标签: .net asp.net sql-server sql-server-2005

过去几天我们在网站上看到的错误信息太多了:

  

“超时已过期。超时时间   在获得之前经过了   从游泳池连接。这可能   已经发生,因为所有汇集   连接正在使用和最大池   达到了规模。“

我们的代码暂时没有改变任何内容。我修改了代码以检查未关闭的打开连接,但发现一切都没问题。

  • 我该如何解决这个问题?

  • 我是否需要编辑此池?

  • 如何编辑此池的最大连接数?

  • 高流量网站的推荐价值是什么?


更新

我是否需要在IIS中编辑某些内容?

更新

我发现活动连接数在15到31之间,我发现SQL Server中配置的最大连接数超过3200个连接,31个太多或者我应该在ASP中编辑一些东西.NET configration?

23 个答案:

答案 0 :(得分:182)

在大多数情况下,连接池问题与“连接泄漏”有关。您的应用程序可能无法正确且一致地关闭其数据库连接。当您打开连接时,它们将保持阻塞状态,直到.NET垃圾收集器通过调用Finalize()方法为您关闭它们。

您希望确保真正关闭连接。例如,如果.OpenClose之间的代码抛出异常,则以下代码将导致连接泄漏:

var connection = new SqlConnection(connectionString);
connection.Open();
// some code
connection.Close();                

正确的方法是:

var connection = new SqlConnection(ConnectionString);
try
{
     connection.Open();
     someCall (connection);
}
finally
{
     connection.Close();                
}

using (SqlConnection connection = new SqlConnection(connectionString))
{
     connection.Open();
     someCall(connection);
}

当您的函数从类方法返回连接时,请确保在本地缓存它并调用其Close方法。您将使用此代码泄漏连接,例如:

var command = new OleDbCommand(someUpdateQuery, getConnection());
result = command.ExecuteNonQuery();
connection().Close(); 

第一次拨打getConnection()时返回的连接未被关闭。

,而不是关闭你的连接,这一行创建一个新的并尝试关闭它

如果您使用SqlDataReaderOleDbDataReader,请关闭它们。即使关闭连接本身似乎也可以做到这一点,但在使用它们时需要额外的努力来明确地关闭数据读取器对象。


来自MSDN / SQL Magazine的这篇文章“Why Does a Connection Pool Overflow?”解释了很多细节并提出了一些调试策略:

  • 运行sp_whosp_who2。这些系统存储过程从sysprocesses系统表返回信息,该信息显示所有工作进程的状态和信息。通常,您将看到每个连接一个服务器进程ID(SPID)。如果使用连接字符串中的Application Name参数命名连接,则很容易找到工作连接。
  • 将SQL Server Profiler与SQLProfiler TSQL_Replay模板一起使用以跟踪打开的连接。如果您熟悉Profiler,则此方法比使用sp_who进行轮询更容易。
  • 使用性能监视器监视池和连接。我马上讨论这个方法。
  • 在代码中监控性能计数器。您可以使用例程提取计数器或使用新的.NET PerformanceCounter控件来监视连接池的运行状况和已建立连接的数量。

答案 1 :(得分:27)

安装.NET Framework v4.6.1后,我们与远程数据库的连接立即开始超时due to this change

要修复,只需在连接字符串中添加参数TransparentNetworkIPResolution并将其设置为 false

  

服务器= myServerName;数据库= MYDATABASE; Trusted_Connection = TRUE;的 TransparentNetworkIPResolution =假

答案 2 :(得分:13)

除非您的使用量增加,否则似乎不太可能存在积压工作。 IMO,最可能的选择是某些东西正在使用连接而不是立即释放它们。您是否确定在所有情况下都使用using?或者(通过什么机制)释放连接?

答案 3 :(得分:13)

在关闭连接或datareader之前,您是否检查过未关闭的DataReader和response.redirects?在重定向之前不关闭它们时,连接会保持打开状态。

答案 4 :(得分:9)

我们也会在我们的网站上不时遇到这个问题。在我们的案例中,罪魁祸首是我们的统计/指数已经过时。这导致先前快速运行的查询(最终)变得缓慢和超时。

尝试更新统计信息和/或重建受查询影响的表上的索引,看看是否有帮助。

答案 5 :(得分:5)

在我的一个.NET应用程序中使用某些第三方数据层时,我也遇到过这个问题。问题是该层没有正确关闭连接。

我们抛弃了图层并自己创建了一个图层,它总是关闭并处理连接。从那时起,我们不再收到错误了。

答案 6 :(得分:5)

您可以通过在连接字符串中指定MinPoolSize=xyz和/或MaxPoolSize=xyz来指定最小和最大池大小。然而,问题的原因可能是另一回事。

答案 7 :(得分:4)

您也可以尝试解决超时问题:

如果您没有将httpRuntime添加到您的网站配置中,请在<system.web>标记中添加

<sytem.web>
     <httpRuntime maxRequestLength="20000" executionTimeout="999999"/>
</system.web>

像这样修改你的连接字符串;

 <add name="connstring" connectionString="Data Source=DSourceName;Initial Catalog=DBName;Integrated Security=True;Max Pool Size=50000;Pooling=True;" providerName="System.Data.SqlClient" />

最后使用

    try
    {...} 
    catch
    {...} 
    finaly
    {
     connection.close();
    }

答案 8 :(得分:3)

在我的情况下,由于使用async / await而发生了另​​一个原因,导致了相同的错误消息:

System.InvalidOperationException:超时已过期。从池中获取连接之前已经过超时时间。这可能是因为所有池化连接都在使用中,并且达到了最大池大小。'

只是对发生的事情(以及我如何解决)进行了简要概述,希望这对以后的工作有所帮助:

找到原因

这一切都发生在具有Dapper和SQL Server的ASP.NET Core 3.1 Web项目中,但我确实认为它独立于那种项目。

首先,我有一个中央功能来获取SQL连接:

internal async Task<DbConnection> GetConnection()
{
    var r = new SqlConnection(GetConnectionString());
    await r.OpenAsync().ConfigureAwait(false);
    return r;
}

我正在数十种方法中使用此功能,例如这个:

public async Task<List<EmployeeDbModel>> GetAll()
{
    await using var conn = await GetConnection();
    var sql = @"SELECT * FROM Employee";

    var result = await conn.QueryAsync<EmployeeDbModel>(sql);
    return result.ToList();
}

如您所见,我正在使用不带花括号(using{)的新}语句,因此连接的处理在功能。

仍然,我收到有关池中不再有可用连接的错误。

我开始调试应用程序,并在发生异常时停止运行。当它停止时,我首先看了 Call Stack 窗口,但这只显示了System.Data.SqlClient内部的某个位置,对我没有真正的帮助:

enter image description here

接下来,我看了 Tasks 窗口,它提供了更好的帮助:

enter image description here

在“等待”或“计划”状态下,实际上有成千上万次调用我自己的GetConnection方法。

Tasks 窗口中双击这样的行时,它通过 Call Stack 窗口向我显示了代码中的相关位置。

这有助于我找出这种行为的真正原因。它在下面的代码中(出于完整性考虑):

[Route(nameof(LoadEmployees))]
public async Task<IActionResult> LoadEmployees(
    DataSourceLoadOptions loadOption)
{
    var data = await CentralDbRepository.EmployeeRepository.GetAll();

    var list =
        data.Select(async d =>
            {
                var values = await CentralDbRepository.EmployeeRepository.GetAllValuesForEmployee(d);
                return await d.ConvertToListItemViewModel(
                    values,
                    Config,
                    CentralDbRepository);
            })
            .ToListAsync();
    return Json(DataSourceLoader.Load(await list, loadOption));
}

在上述控制器操作中,我首先调用了EmployeeRepository.GetAll(),以从数据库表“ Employee”中获取模型列表。

然后,对于每个返回的模型(即结果集的每一行),我再次对EmployeeRepository.GetAllValuesForEmployee(d)进行了数据库调用。

尽管这在性能上非常糟糕,但在异步上下文中,它的行为是吞噬了连接池连接而没有适当释放它们。

解决方案

我通过在外部SQL查询的内部循环中删除SQL查询来解决它。

这可以通过完全省略它来完成,或者如果需要,可以将其移动到外部SQL查询中的一个/多个JOIN上,以从数据库中一次获取所有 数据单个SQL查询。

tl; dr /经验教训

不要在短时间内执行大量SQL查询,尤其是在使用async / await时。

答案 9 :(得分:3)

这主要是由于应用程序中未关闭连接。在连接字符串中使用“MinPoolSize”和“MaxPoolSize”。

答案 10 :(得分:3)

就我而言,我没有关闭DataReader对象。

        using (SqlCommand dbCmd = new SqlCommand("*StoredProcedureName*"))
        using (dbCmd.Connection = new SqlConnection(WebConfigurationAccess.ConnectionString))
            {
            dbCmd.CommandType = CommandType.StoredProcedure;

            //Add parametres
            dbCmd.Parameters.Add(new SqlParameter("@ID", SqlDbType.Int)).Value = ID;
.....
.....
            dbCmd.Connection.Open();
            var dr = dbCmd.ExecuteReader(); //created a Data reader here
            dr.Close();    //gotta close the data reader
            //dbCmd.Connection.Close(); //don't need this as 'using' statement should take care of this in its implicit dispose method.
            }

答案 11 :(得分:2)

不要太多次实例化sql连接。打开一个或两个连接,并将它们用于所有下一个sql操作。

即使在Dispose连接时,也会抛出异常。

答案 12 :(得分:2)

使用此:

finally
{
    connection.Close();
    connection.Dispose();
    SqlConnection.ClearPool();
}

答案 13 :(得分:2)

如果您正在处理复杂的遗留代码,其中无法使用简单的使用(..){..} - 就像我一样 - 您可能需要查看我在此SO question中发布的代码段用于在连接可能泄露时确定连接创建的调用堆栈的方法(在设置超时后未关闭)。这使得很容易发现泄漏的原因。

答案 14 :(得分:1)

我的代码中出现了这个问题。我将粘贴一些示例代码,我遇到了以下错误。 从池中获取连接之前经过的超时时间。这可能是因为所有池连接都在使用中并且达到了最大池大小。

 String query = "insert into STATION2(ID,CITY,STATE,LAT_N,LONG_W) values('" + a1 + "','" + b1 + "','" + c1 + "','" + d1 + "','" + f1 + "')";
    //,'" + d1 + "','" + f1 + "','" + g1 + "'

    SqlConnection con = new SqlConnection(mycon);
    con.Open();
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = query;
    cmd.Connection = con;
    cmd.ExecuteNonQuery();
    **con.Close();**

您希望每次都关闭连接。在此之前我没有我们的密切连接由于这个我得到错误。 添加关闭语句后,我已经结束了此错误

答案 15 :(得分:1)

除了发布的解决方案....

在处理1000页遗留代码时,每次都会多次调用一个共同的GetRS,这是另一种解决问题的方法:

在现有的常见DLL中,我们添加了 CommandBehavior.CloseConnection 选项:

    static public IDataReader GetRS(String Sql)
    {
        SqlConnection dbconn = new SqlConnection(DB.GetDBConn());
        dbconn.Open();
        SqlCommand cmd = new SqlCommand(Sql, dbconn);
        return cmd.ExecuteReader(CommandBehavior.CloseConnection);   
    }

然后在每个页面中,只要您关闭数据阅读器,连接也会自动关闭,从而防止连接泄漏。

    IDataReader rs = CommonDLL.GetRS("select * from table");
    while (rs.Read())
    {
        // do something
    }
    rs.Close();   // this also closes the connection

答案 16 :(得分:1)

我只是遇到了同样的问题,想分享帮助我找到来源的内容: 将应用程序名称添加到您的连接字符串中,然后监视与SQL Server的打开的连接

select st.text,
    es.*, 
    ec.*
from sys.dm_exec_sessions as es
    inner join sys.dm_exec_connections as ec on es.session_id = ec.session_id
    cross apply sys.dm_exec_sql_text(ec.most_recent_sql_handle) st
where es.program_name = '<your app name here>'

答案 17 :(得分:1)

我的 AWS EC2 实例上也有这个确切的错误日志。

没有连接泄漏,因为我只是在部署 alpha 应用程序(没有真正的用户),并且我与 Activity Monitor 和 sp_who 确认实际上没有连接到数据库。

我的问题与 AWS 相关 - 更具体地说,与安全组有关。看,只有某些安全组可以访问我托管数据库的 RDS 服务器。 我使用 authorize-security-group-ingress 命令添加了一个入口规则,以允许使用 --source-group-name 参数访问正确的 EC2 实例到 RDS 服务器。添加了入口规则,我可以在 AWS UI 上看到它 - 但我收到了这个错误。

当我在 AWS UI 上手动删除并添加入口规则时 - 突然异常消失了,应用程序正在运行。

答案 18 :(得分:0)

我之前遇到的这个问题。它最终成为防火墙的问题。我刚给防火墙添加了规则。我必须打开端口1433,以便SQL服务器可以连接到服务器。

答案 19 :(得分:0)

在我的情况下,我遇到了无限循环(通过试图从数据库获取值的get属性),该循环不断打开数百个Sql连接。

while (true)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        someCall(connection);
    }
}

答案 20 :(得分:0)

是的,有一种方法可以更改配置。如果您在专用服务器上并且仅需要更多的SQL连接,则可以按照以下说明更新两个连接字符串中的“最大池大小”条目:

  1. 使用远程桌面登录服务器
  2. 打开“我的电脑”(Windows-E)并转到C:\ inetpub \ vhosts [domain] \ httpdocs
  3. 双击web.config文件。如果文件结构设置为隐藏扩展名,则可能仅列为Web。这将打开Visual Basic或类似的编辑器。
  4. 找到您的连接字符串,它们类似于以下示例:

    “ add name =” SiteSqlServer“ connectionString =” server =(local); database = dbname; uid = dbuser; pwd = dbpassword; pooling = true;连接寿命= 120;最大池大小= 25;“”

5。将最大池大小= X值更改为所需的池大小。

  1. 保存并关闭您的web.config文件。

答案 21 :(得分:0)

确保为连接池设置了正确的设置。正如我在以下文章中解释的那样,这非常重要: https://medium.com/@dewanwaqas/configurations-that-significantly-improves-your-app-performance-built-using-sql-server-and-net-ed044e53b60 如果遵循它,您的应用程序的性能将得到极大的改善。

答案 22 :(得分:0)

一开始我并不认为这是我的问题,但在浏览此列表时,我发现它没有涵盖我的问题所在。

我的问题是我有一个错误,它尝试使用实体框架多次写入相同的记录。它不应该这样做;这是我的错误。看看你正在写的数据。我的想法是 SQL 正忙于写记录,可能锁定并创建超时。在我修复了试图在连续尝试中多次写入记录的代码区域后,错误消失了。