You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ADO.NET/C#连接SQL Server未释放,2000+连接致CPU100%求解决

问题排查与解决方案

核心问题分析

  1. 连接未正确归还到连接池
    你手动调用了_dbContext.Database.GetDbConnection().Open()打开连接,但未主动关闭。虽然SqlDataAdapter.Fill()会自动处理连接开闭,但仅在连接原本关闭时生效——如果是你手动打开的连接,Fill()不会帮你关闭,导致连接长期处于打开状态,无法被连接池回收。时间一长,连接数会突破池上限,甚至超出Max Pool Size=1000的设置,最终耗尽系统资源导致CPU拉满。

  2. 连接池配置与异常处理的隐患

  • connection timeout=0意味着请求连接时会无限等待,一旦连接池耗尽,大量线程会挂起等待,直接拉高CPU占用。
  • catch块里throw ex会清空异常的原始栈信息,导致无法定位到底是哪一步出问题导致连接未释放。
  1. DbContext生命周期可能存在问题
    如果_dbContext是单例或者生命周期过长,它持有的连接会被长期占用,无法正常回收。

修复步骤

1. 修正连接管理代码

彻底移除手动打开连接的逻辑,让SqlDataAdapter和EF自动处理连接:

DataTable dataTable = new DataTable();

try
{
    using (var command = _dbContext.Database.GetDbConnection().CreateCommand())
    {
        command.CommandText = "usp_get_userdetails";
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@UserId", userid));

        // 移除手动判断连接状态和打开的代码
        var dataAdapter = new SqlDataAdapter { SelectCommand = (SqlCommand)command };
        dataAdapter.Fill(dataTable); // 直接填充DataTable,省去DataSet的冗余
    }
}
catch (Exception ex)
{
    // 直接throw保留原始异常栈,不要用throw ex
    throw;
}

return dataTable;

SqlDataAdapter.Fill()的逻辑是:如果连接未打开,就打开它,操作完成后关闭;如果连接已经打开,就保持打开状态。只要不手动打开连接,它会自动完成连接的回收。

2. 调整连接字符串配置

server=*******;uid=username;pwd=********;database=DBName;connection timeout=30;Max Pool Size=1000;TrustServerCertificate=True;MultipleActiveResultSets=True
  • 把connection timeout=0改成30(或你业务允许的超时时间),避免线程无限等待。
  • 去掉database= DBName里的空格,防止配置解析出错。
  • 保留Pooling=True(默认值,连接池是提升性能的关键)。

3. 排查数据库端的长期连接

用以下SQL查询当前数据库的活跃连接,看哪些连接长时间未释放,以及对应的执行语句:

SELECT 
    c.session_id, 
    c.connect_time, 
    s.host_name, 
    s.program_name,
    s.login_name, 
    c.status, 
    t.text AS last_sql
FROM sys.dm_exec_connections c
JOIN sys.dm_exec_sessions s ON c.session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(c.most_recent_sql_handle) t
WHERE c.database_id = DB_ID('DBName')
ORDER BY c.connect_time DESC;

重点看usp_get_userdetails是否存在执行缓慢的情况——如果存储过程跑太久,连接会被一直占用,同样会导致连接池耗尽。

4. 确认DbContext的生命周期

如果是ASP.NET(Core)项目,确保_dbContext是Scoped生命周期(每个请求创建一个实例),不要用Singleton。单例DbContext会长期持有连接,完全破坏连接池的回收机制。


内容的提问来源于stack exchange,提问作者Vikas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 09:37:03