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

.NET Core控制台程序Parallel.ForEach操作SQL Server时出现连接错误

解决方案:并行插入SQL Server时间歇性连接失败问题

这个问题我之前处理过类似场景,核心原因大概率是连接池资源耗尽或者过高的并行度引发数据库连接竞争,结合你的代码和使用场景,给你几个针对性的优化方案:

1. 限制并行度,避免连接池过载

Parallel.ForEach默认会根据CPU核心数设置并行度,但数据库操作是IO密集型任务,过高的并发会瞬间创建大量数据库连接,超出.NET连接池的默认上限(默认Max Pool Size=100),导致后续请求等待连接超时失败。

你可以显式限制并行度,比如根据服务器性能设置为CPU核心数的2倍,或者更小的固定值(比如10-20),测试找到最优值:

var parallelOptions = new ParallelOptions
{
    // 限制并发数,避免连接池被打满
    MaxDegreeOfParallelism = Math.Min(15, Environment.ProcessorCount * 2)
};

Parallel.ForEach(collection, parallelOptions, (item) => 
{ 
    var results = item.Calculate(parameters); 
    dal.Results.Insert(results); 
    allResults.AddRange(results); 
});

2. 优化数据库操作:减少事务和连接持有时间

你的代码中每个Insert都开启独立事务,会延长连接的持有时间,加剧连接池的压力。如果你的插入操作不需要严格的原子性(比如单批插入失败不影响其他批次),可以直接去掉事务,大幅减少连接占用时间:

public override void Insert(IEnumerable<Result> entities) 
{ 
    using (var connection = GetConnection()) 
    { 
        connection.Open(); 
        // 去掉事务,直接批量插入
        connection.Insert(entities); 
    } 
}

如果必须保证单批插入的原子性,建议先并行计算所有结果,再统一批量插入(用一个连接和事务),这样能大幅减少连接创建次数:

// 第一步:并行计算所有结果,存入线程安全的ConcurrentBag
Parallel.ForEach(collection, parallelOptions, (item) => 
{ 
    var results = item.Calculate(parameters); 
    allResults.AddRange(results); 
});

// 第二步:统一批量插入,仅使用一个连接和事务
using (var connection = GetConnection())
{
    connection.Open();
    using (var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted))
    {
        connection.Insert(allResults, transaction);
        transaction.Commit();
    }
}

3. 添加重试机制,处理间歇性连接错误

对于网络波动、连接池短暂耗尽这类间歇性错误,添加重试逻辑能有效解决问题。可以用Polly库实现重试策略(先安装Polly NuGet包):

public override void Insert(IEnumerable<Result> entities) 
{ 
    // 针对连接相关的SqlException进行重试,最多3次,每次间隔指数递增
    var retryPolicy = Policy
        .Handle<SqlException>(ex => 
            ex.Number == 10054 || // 连接被远程重置
            ex.Number == 4060 || // 无法连接到服务器
            ex.Message.Contains("network-related") || 
            ex.Message.Contains("instance-specific"))
        .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));

    retryPolicy.Execute(() =>
    {
        using (var connection = GetConnection()) 
        { 
            connection.Open(); 
            using (var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted)) 
            { 
                connection.Insert(entities, transaction); 
                transaction.Commit(); 
            } 
        }
    });
}

4. 检查连接池和SQL Server配置

  • 可以在连接字符串中调整Max Pool Size参数(比如设置为200),但不要超过SQL Server的最大连接数限制(SQL Server默认最大连接数为32767,可通过SELECT @@MAX_CONNECTIONS查看):
    Server=yourServer;Database=yourDB;Integrated Security=True;Max Pool Size=200;
    
  • 运行SELECT * FROM sys.dm_exec_connections查看SQL Server当前的连接数,确认是否达到瓶颈。

总结

优先从限制并行度和优化数据库操作入手,这两个调整能直接缓解连接池压力;再配合重试机制处理间歇性错误,基本就能解决你遇到的问题。如果还是有问题,建议查看SQL Server的错误日志,定位具体的错误码,进一步排查。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:38:38