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


211

最近几天,我们在网站上看到此错误消息太多:

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

我们已经有一段时间没有在代码中进行任何更改了。我修改了代码以检查未关闭的打开的连接,但发现一切都很好。

  • 我该如何解决?

  • 我需要编辑该池吗?

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

  • 高流量网站的建议值是多少?


更新:

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

更新:

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


如果我没有记错的话,最大池大小的默认值是100。大多数网站在高负载下使用的连接数都不超过50个,具体取决于查询完成所需的时间。连接字符串中的短期修复:尝试在连接字符串中设置一个更高的值:“最大池大小= ...”
splattne

多少?例如使其达到200?
Amr Elgarhy,2009年

3
我认为您应该真正寻找导致问题的原因。您的查询(或其中一些)是否运行很长时间?
splattne

可能是多数民众赞成在真正的原因,一个查询要花很多时间执行,我将在其中搜索,谢谢
Amr Elgarhy 09年

1
希望您能找到问题所在。一个建议:如果您使用的是SQL Server,请尝试“ SQL Profiler”并查找较长的查询:sql-server-performance.com/articles/per/…–
splattne

Answers:


218

在大多数情况下,连接池问题与“连接泄漏”有关。您的应用程序可能无法正确且一致地关闭其数据库连接。当您保持连接打开状态时,它们将保持阻塞状态,直到.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上的文章“ 为什么连接池会溢出? ”解释了很多细节并提出了一些调试策略:

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

4
一个小小的修正:GC从不调用对象的Dispose方法,仅调用其终结器(如果有)。然后,终结者可以在必要时对Dispose进行“后备”调用,尽管我不确定SqlConnection是否这样做。
路加福音

1
当我们必须将最大池大小设置为50并且只有很少的用户时,性能是否会降低?
mahesh sharma

37

安装.NET Framework v4.6.1后,由于此更改,我们到远程数据库的连接立即开始超时。

要解决此问题,只需TransparentNetworkIPResolution在连接字符串中添加参数并将其设置为false即可

Server = myServerName; Database = myDataBase; Trusted_Connection = True; TransparentNetworkIPResolution =否


在这种情况下,问题是“在从池中获取连接之前经过了超时时间”。您的连接字符串修复了此问题,还是一个单独的握手问题?
FBryant87 '18

1
据我所知,这是与问题完全相同的错误消息。更新到.NET Framework v4.6.1后立即发生。
ajbeaven

我也是这种情况,在我在Azure上运行的应用程序服务上为我修复了该问题,并连接到Azure SQL数据库。我在使用Dapper并正确处理了连接,但是仍然收到“从池中获取连接之前已经过超时时间”错误消息。但是没有更多了,所以谢谢@ajbeaven
史蒂夫·肯纳尔德

13

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


13

在关闭连接或数据读取器之前,是否检查了未关闭的DataReader和response.redirects。当您在重定向之前不关闭连接时,连接保持打开状态。


3
+1-或返回DataReaders的函数-连接将永远不会在您创建它们的函数之外关闭...
splattne,2009年

1
如果您的函数返回SqlDataReader,则最好将其转换为DataTable而不是增加最大池大小
live-love

10

我们也时常在我们的网站上遇到此问题。我们的罪魁祸首是我们的统计数据/指数过时了。这导致先前快速运行的查询(最终)变得缓慢且超时。

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


3
我认为这可以解释为什么查询可能超时,但是我认为这不能解释为什么在尝试获得连接时经历超时。
DrGriff 2014年

1
索引错误可能是查询花费了更长的时间,并且同时使用了更多的连接。
LosManos

1
在数据库上使用sp_updatestats更新所有统计信息后为我工作:EXEC sp_updatestats;
boateng 2015年

6

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


3
推荐的MaxPoolSize是多少?
Amr Elgarhy 09年

2
最好的方法可能是不要指定它,除非您有特殊要求并且您知道针对特定情况的合适池大小。
Mehrdad Afshari,2009年

1
注意:我检查了一下,发现与数据库的活动连接大约是22个,那太多了吗?
Amr Elgarhy,2009年

我不这么认为。我认为默认的池大小是100个连接。它取决于网络和SQL Server上每个连接的负载。如果这些查询运行繁重,则可能会导致问题。另外,启动新连接时可能会出现网络问题,并可能导致该异常。
Mehrdad Afshari 2009年

5

在我的.NET应用程序之一中使用某些第三方数据层时,我也遇到了此问题。问题在于该层未正确关闭连接。

我们将层拉出并自己创建一个层,该层始终关闭并布置连接。从那时起,我们不再收到错误。


我们正在使用LLBL,并且该网站运行了2年,而最近几天才开始出现这种情况。
Amr Elgarhy,2009年

5

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

如果您没有将httpRuntime添加到您的Webconfig中,请在<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();
    }

3

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


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.
            }

2

如果您正在处理复杂的旧版代码,而无法像以前一样简单地使用using(..){..},则可能需要查看我在此SO问题中发布的代码段,以确定确定连接潜在泄漏(在设置的超时后未关闭)时,创建连接的调用堆栈。这样就很容易发现泄漏的原因。


2

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

似乎即使在Dispose建立连接时也会引发异常。


2

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

在处理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

2

我只是遇到了同样的问题,想分享帮助我找到源的内容:将应用程序名称添加到您的连接字符串中,然后管理与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>'

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();**

您想每次都关闭连接。在此之前,由于这个原因,我没有与我们保持紧密联系,但出现错误。添加结束语句后,我已经结束了此错误


0

您的代码泄漏了连接。您可以尝试使用using来证明您正在关闭它们。

 Using (SqlConnection sqlconnection1 = new SqlConnection(“Server=.\\SQLEXPRESS ;Integrated security=sspi;connection timeout=5”)) {
                          sqlconnection1.Open();
                          SqlCommand sqlcommand1 = sqlconnection1.CreateCommand();
                          sqlcommand1.CommandText = raiserror (‘This is a fake exception’, 17,1)”;
                          sqlcommand1.ExecuteNonQuery();  //this throws a SqlException every time it is called.
                          sqlconnection1.Close(); //Still never gets called.
              } // Here sqlconnection1.Dispose is _guaranteed_

https://blogs.msdn.microsoft.com/angelsb/2004/08/25/connection-pooling-and-the-timeout-expired-exception-faq/



0

用这个:

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

12
也许我错过了要点SqlConnection.ClearPool,但这只是防止您当前的连接释放回连接池吗?我认为连接池的想法是允许更快的连接。确保每次完成连接后都会从池中释放该连接,这意味着每次需要一个新连接时都需要创建一个新连接,而不是从池中拉出备用连接?请解释该技术的用途以及用途。
Dib

0

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

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

    “添加名称=” SiteSqlServer“ connectionString =”服务器=(本地);数据库= dbname; uid = dbuser; pwd = dbpassword;池= true;连接寿命= 120;最大池大小= 25;“

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

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


0

就我而言,我遇到了无限循环(通过试图从数据库获取值的get Property),该循环不断打开数百个Sql连接。

要重现该问题,请尝试以下操作:

while (true)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        someCall(connection);
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.