让 .net SqlConnection 无限期打开与使用块

Leaving .net SqlConnection open indefinitely vs a using block

我有一个深入的问题。我想了解为什么 SqlConnection 正在关闭以及如何检测连接池是否实际发生。

我有两个不同版本的 class,它们抽象地与数据库对话并返回 DataTable。我提供的是 problem/question 的示例。

版本 1 在实例化时打开 SqlConnection 并保持打开状态。这是一个单线程进程,它处理传入的消息并尽快将结果写入数据库。这就是让 SqlConnection 永久打开的想法:

public class SpecialDbClientVersion1 : IDisposable
{
    string connStr;
    SqlConnection dbConn;

    public SpecialDbClientVersion1(string connStr)
    {
        this.connStr = connStr;
        this.dbConn = new SqlConnection(connStr);

        try
        {
            dbConn.Open();
        }
        catch (SqlException ex)
        {
            throw new Exception($"An exception occurred while trying to connect to {dbConn.Database}", ex);
        }
    }

    public DataTable QuerySqlReturnResultAsDataTable(string sql, int commandTimeout = 30)
    {
        try
        {
            var cmd = new SqlCommand(sql, dbConn);
            cmd.CommandType = CommandType.Text;
            cmd.CommandTimeout = commandTimeout;
            var dt = new DataTable();
            var adapter = new SqlDataAdapter(cmd);
            adapter.Fill(dt);
            return dt;
        }
        catch (Exception e)
        {
            throw new InvalidOperationException(e.Message);
        }
    }

    public void Dispose()
    {
        dbConn.Dispose();
    }
}

当我对此进行测试时,一切正常,并且吞吐量非常高。最终,尽管 SqlConnection 由于某种原因被关闭。抛出错误,聚会结束。

问题 1:谁能解释一下在我不调用 Close() 方法的情况下是什么导致此连接进入 Closed 状态?

现在版本 2 在 using 块中执行所有工作。根据互联网,运行时应该在后台神奇地使用连接池。这是 Microsoft 处理 SqlConnection 个对象的最佳实践。使用连接池应该不会对性能造成太大影响。

public class SpecialDbClientVersion2
{
    string connStr;
    SqlConnection dbConn;

    public SpecialDbClientVersion2(string connStr)
    {
        this.connStr = connStr;
    }

    public DataTable QuerySqlReturnResultAsDataTable(string sql, int commandTimeout = 30)
    {
        using(var dbConn = new SqlConnection(connStr))
        {
            try
            {
                var cmd = new SqlCommand(sql, dbConn);
                cmd.CommandType = CommandType.Text;
                cmd.CommandTimeout = commandTimeout;
                var dt = new DataTable();
                var adapter = new SqlDataAdapter(cmd);
                adapter.Fill(dt);
                return dt;
            }
            catch (Exception e)
            {
                throw new InvalidOperationException(e.Message);
            }
        }
    }
}

问题 2:我如何判断是否实际使用了连接池?写入数据库需要尽可能快,我担心版本 2 每次都必须重新建立与数据库的连接。这会破坏性能。我如何衡量连接池是否正在发生以及它是否正在影响服务的吞吐量?

我一直在寻找关于这个主题的深入研究资源,但我发现的大部分内容只是 "Use a SqlConnection in a using block and everything will be fine." 我正在寻找比这更深入的解释。我真的很想了解这里发生的事情的核心,以便我找到正确的性能问题。感谢您提供的任何见解。

问题 1 可以通过与池关联的空闲超时这一事实来回答。

问题 2:默认情况下连接池设置为 true,因此如果您没有在连接字符串中明确关闭它,您可以假设您正在使用它。我认为没有办法确定池中还剩下多少连接。如果您每次都使用完全相同的连接字符串,那么您可以放心地假设它们在同一个池中。

阅读本文档以更深入地了解连接池。

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling

您必须在 SQL 服务器级别监视连接。

SELECT 
DB_NAME(dbid) as DBName, 
COUNT(dbid) as NumberOfConnections,
loginame as LoginName
FROM sys.sysprocesses
WHERE bid > 0
GROUP BY dbid, loginame;

正如其他人所提到的,连接可能在服务器端关闭或因空闲超时而关闭,请查看服务器端日志以获取详细信息。

对于问题的第二部分,有性能计数器 tracks connection pool information。这可以通过代码查看(参见链接页面的示例)设置 运行 程序的进程 ID 并显示 NumberOfPooledConnectionsNumberOfNonPooledConnections 之类的值以查看连接池是否实际正在被进程使用。

通过打开 off by default counters,您还可以监视 NumberOfFreeConnections,它会告诉您是否有可用的开放连接供您的程序获取并用于获得即时响应而无需额外开销打开您要求的连接。