无法连接到 SQL 服务器会话数据库异常 - 所有池连接都在使用中

Unable to connect to SQL Server session database exception - All pooled connections were in use

我在生产时突然在我的日志文件中收到此错误:

System.Web.HttpException (0x80004005): Exception of type 'System.Web.HttpException' was thrown.

System.Web.HttpException (0x80004005): Unable to connect to SQL Server session database.

System.InvalidOperationException: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.

我看了很多其他答案,比如 here

但我没有连接泄漏,我不想只将最大池设置为 200左右因为我想明白为什么突然出现这个异常...

这些是我的连接字符串:

<!--Nhibernate-->
<add name="name" 
     connectionString="Server= servername;Initial Catalog=DBname;UID=username;Password=password;MultipleActiveResultSets=true" 
     providerName="System.Data.SqlClient" /> 

<!--Entity Framework-->
<add name="name" 
     connectionString= "metadata=res://*/Models.Model1.csdl|res://*/Models.Model1.ssdl|res://*/Models.Model1.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=servername;initial catalog=DBname;user id=userName;password=password;multipleactiveresultsets=True;App=EntityFramework&quot;" 
     providerName="System.Data.EntityClient" /> 

更新

一个使用没有连接泄漏的数据库的例子:

using (var db = new dbName())
{
    using (var connection = db.Database.Connection)
    {
       var command = connection.CreateCommand();

       ...

       connection.Open();

       using (var reader = command.ExecuteReader())
       {
          ...
          reader.NextResult();
          ...
          reader.NextResult();
          ...
          reader.NextResult();
          ...
       }

       connection.Close();
    }
}  

更新

事实证明,我在 Entity Framework 中确实有 连接泄漏 , 没有使用的地方using,连接也没有关闭!

示例:

private DbContext context = new DbContext();

...

List<dbObject> SeriesEvents = context.dbObject.Where(e => e.RecurrenceId == entity.RecurrenceId).ToList();

context 变量没有关闭。

我更关心的是这个查询使很多数据库查询超过了 100。

通常这种连接池问题伴随着连接泄漏。数据库操作可能会发生一些异常,连接可能无法正常关闭。 请添加一个 try{} catch{} finally{} 块并在 finally 块中关闭连接。

如果您使用 using,则 try catch finally 是隐式的,运行时本身将关闭连接。所以你不需要明确关闭连接。如果使用 using 块,请从您的代码中删除 connection.Close()

Using 语句实际上是 try{} catch{} finally{} 的语法糖,其中连接由运行时关闭并在 finally 中处理。

try
{
     connection.Open();
     // DB Operations
}
finally
{
     connection.Close();                
}

错误消息表明会话状态数据库出现问题,但您提供的信息是针对应用程序连接的。由于不同的连接字符串,会话状态池不同。

会话状态问题的常见原因是 1) 托管会话状态数据库的实例对于工作负载来说容量不足或 2) 会话状态清理作业导致长期阻塞。

早期 .NET 版本中的清理作业在单个批次中删除了所有过期的会话,并且因在繁忙的站点上导致长期阻塞而臭名昭著,尤其是在大量使用会话状态时:

CREATE PROCEDURE dbo.DeleteExpiredSessions
AS
    DECLARE @now datetime
    SET @now = GETUTCDATE()

    DELETE [ASPState].dbo.ASPStateTempSessions
    WHERE Expires < @now

    RETURN 0

后来的 .NET 版本使用游标进行删除,大大提高了并发性。

CREATE PROCEDURE dbo.DeleteExpiredSessions
AS
    SET NOCOUNT ON
    SET DEADLOCK_PRIORITY LOW 

    DECLARE @now datetime
    SET @now = GETUTCDATE() 

    CREATE TABLE #tblExpiredSessions 
    ( 
        SessionId nvarchar(88) NOT NULL PRIMARY KEY
    )

    INSERT #tblExpiredSessions (SessionId)
        SELECT SessionId
        FROM [ASPState].dbo.ASPStateTempSessions WITH (READUNCOMMITTED)
        WHERE Expires < @now

    IF @@ROWCOUNT <> 0 
    BEGIN 
        DECLARE ExpiredSessionCursor CURSOR LOCAL FORWARD_ONLY READ_ONLY
        FOR SELECT SessionId FROM #tblExpiredSessions 

        DECLARE @SessionId nvarchar(88)

        OPEN ExpiredSessionCursor

        FETCH NEXT FROM ExpiredSessionCursor INTO @SessionId

        WHILE @@FETCH_STATUS = 0 
            BEGIN
                DELETE FROM [ASPState].dbo.ASPStateTempSessions WHERE SessionId = @SessionId AND Expires < @now
                FETCH NEXT FROM ExpiredSessionCursor INTO @SessionId
            END

        CLOSE ExpiredSessionCursor

        DEALLOCATE ExpiredSessionCursor

    END 

    DROP TABLE #tblExpiredSessions

RETURN 0

如果问题是由于实例规模过小造成的,并且扩展不可行,请考虑使用会话状态分区进行扩展。