.NET 3.5下SQL查询偶发远程主机强制关闭现有连接异常
问题根因
这个偶发报错是.NET Framework 3.5自带的System.Data.SqlClient的已知行为差异,.NET 4.0及以上版本修复了连接池的空闲连接校验逻辑,所以不会复现:
- .NET 3.5下的SqlClient连接池默认不会校验取出的空闲连接是否存活,SQL Server服务端会按配置自动关闭闲置超过阈值(默认30分钟,部分实例会配置得更短)的TCP连接。你每次调用方法新建SqlConnection时,实际是从连接池复用已有TCP连接,一旦取到已经被服务端单方面关闭的“死连接”,执行
ExecuteReader()时就会抛出这个远端强制关闭连接的传输层错误。 - 因为连接池里的死连接是随机被取到的,所以报错呈现偶发特征,和你描述的“两次操作成一次”的概率完全匹配。
修复方案
1. 调整连接字符串配置(改造成本最低)
在你现有连接字符串末尾追加以下参数,让SqlClient从连接池取连接时自动做存活校验:
;Connection Lifetime=3;Pooling=true;Connection Reset=true;Validate Connection=true
Validate Connection=true会在连接取出时发一个极小的探测包确认连接可用,发现是死连接就自动丢弃创建新连接,性能损耗可以忽略,能解决90%以上的偶发断连问题。
2. 增加针对性的错误重试逻辑
连接校验无法覆盖网络瞬断场景,针对你这个获取表名的轻量操作,加1-2次定向重试即可完全消除偶发报错,注意不要捕获到SqlException就直接返回空结果,只对连接类错误重试,其他权限、语法类错误直接记录返回即可。
核心修改后的代码参考:
public List<string> GetAllTables(string sDatabase) { List<string> result = new List<string>(); const int maxRetry = 2; int retryTimes = 0; while (retryTimes <= maxRetry) { try { using (SqlConnection con = new SqlConnection(dbConnection.conString + sDatabase)) { con.Open(); using (SqlCommand command = new SqlCommand("SELECT name FROM sys.Tables", con)) { command.CommandTimeout = 10; using (var reader = command.ExecuteReader()) { while (reader.Read()) { result.Add(reader["name"].ToString()); } } } } return result; } catch (SqlException sqlEx) { // 仅对传输层断连错误做重试,错误号0/10053/10054均对应连接被强制关闭类问题 bool isConnectionError = (sqlEx.Number == 0 || sqlEx.Number == 10053 || sqlEx.Number == 10054); retryTimes++; if (!isConnectionError || retryTimes > maxRetry) { string methodName = new System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Name; string className = new System.Diagnostics.StackTrace().GetFrame(0).GetMethod().DeclaringType.Name; Debug.Print("Error in class {0} - method: {1}: {2}", className, methodName, sqlEx.Message); return result; } result.Clear(); System.Threading.Thread.Sleep(200); } } return result; }
3. 优化调用逻辑减少无效连接
你当前循环5-10次调用GetAllTables属于完全不必要的开销,完全可以在单次恢复操作启动时调用一次该方法,把拿到的表名列表缓存下来,后续流程直接复用缓存结果即可,既减少连接调用次数降低碰死连接的概率,也能提升程序运行速度。
避坑提示
- 不要为了绕开这个问题直接把连接字符串的
Pooling设为false关闭连接池,那样每次调用都会新建TCP连接,性能会下降一个量级,多表恢复时速度会非常慢。 - 检查目标SQL Server实例的连接空闲超时配置,不要设置低于5分钟,否则高频率调用时即使开了连接校验也容易碰到死连接。
内容的提问来源于stack exchange,提问作者aaronwe
相关产品推荐
相关产品推荐

