坑暗花明:又遇 .NET Core 中 System.Data.SqlClient 查询缓慢的问题

在.NET Core项目中,一个看似简单的INNER JOIN操作导致数据库查询性能急剧下降,从几毫秒飙升至数秒。深入调查后发现,问题根源并非JOIN本身,而是SELECT语句中字段数量的微妙变化。随着.NET Core版本的更新,这一性能瓶颈得以解决,揭示了System.Data.SqlClient内部可能存在的重大缺陷。

之前发布过一篇博文 下单快发货慢:一个 JOIN SQL 引起 SqlClient 读取数据慢的奇特问题,当时遇到的问题是从 SQL Server 2008 R2 中查询获取 100 条记录竟然耗时 10 多秒,排查中发现问题与 SQL  查询中包含 INNER JOIN 有关,去掉 INNER JOIN 立马查询飞快。当时天真地以为原因是 JOIN 的那张表数据记录太多以及主表聚集索引不合理,于是采用将 INNER JOIN 部分拆分出来单独查询临时解决了问题。

这两天我们又在另外一个 ASP.NET Core 2.2 项目中也遇到了查询 100 数据库记录速度慢(需要4~6秒)的问题,而且所查询的数据库表数据量并不是很大。通过 EF Core 的 日志记录发现耗时发生在 Executed DbCommand 时,耗时发生点与之前的情况不一样,之前是发生在 Executed DbCommand 之后 SqlDataReader 从数据库读取数据时。

2019-05-11T14:21:38.1015229+08:00 [INFORMATION] Executed DbCommand ("5,850"ms)

而通过 SQL Server Management Studio 执行同样的 SQL 语句只需要 20 毫秒左右,相差200多倍。

Executed DbCommand 日志记录的是 dbCommand.ExecuteReaderAsync 执行的时间(详见 EF Core 的源码

result = new RelationalDataReader(
    connection,
    dbCommand,
    await dbCommand.ExecuteReaderAsync(cancellationToken),
    commandId,
    Logger);

dbCommand.ExecuteReaderAsync 的实现代码在 corefx 的 System.Data.SqlClient 中,如果是 .net core 的问题,那问题就出在 System.Data.SqlClient 。

在上次排查 SqlDataReader 读取数据速度慢问题,就曾怀疑 System.Data.SqlClient ,花了很多时间在 System.Data.SqlClient 的源码中打点排查,最终没有找到线索,这次不敢轻易怀疑它。

虽然这次的耗时发生点与上次不一样,但这次的 SQL 查询语句中有个地方和上次是一样的,也包含 INNER JOIN 查询,于是试着去掉 INNER JOIN ,Executed DbCommand 只需2毫秒。

[INFORMATION] Executed DbCommand ("2"ms)

啊,怎么也与 INNER JOIN 有关,没道理啊,这次 JOIN 的表数据量不大,完全不可能造成 200 多倍的性能之差。看来上次归罪于 INNER JOIN ,可能是冤枉它了,得重新思考与排查这个问题。

由于问题是在某个时间点之后出现,于是采取笨方法,回退 git 提交历史直至问题消失。。。

最终发现,竟然是在一次 git commit 中给这个查询在 SELECT 时增加了1个字段引起的, 去掉这个字段,问题立马消失。进一步测试发现,只要任意去掉 SELECT 中的1个字段,就不会出现问题,太奇怪了。数了数 SELECT 中有20个字段,难道与 SELECT 字段的数量有关?之前的项目会不会也与 SELECT 字段的数量有关?

于是回到之前的项目,恢复 INNER JOIN 查询,这时惊讶地发现 SqlDataReader 读取数据速度慢的问题竟然消失了。回想当时处理问题后到现在所做的变更,唯一的变更就是从 .NET Core 3.0 Preivew 4 升级到 .NET Core 3.0 Preivew 5 ,难道 3.0 Preivew 5 把这个问题给修复了?难道真的是 System.Data.SqlClient 的 bug ?

答案很容易验证,将当前遇到 Executed DbCommand 执行慢的项目升级到 .NET Core 3.0 Preivew 5 ,昨天晚上完成升级后

Executed DbCommand ("3"ms)

飞流直下三千尺,从4秒骤降到3毫秒,相差1000多倍!果然是 System.Data.SqlClient 的一个大 bug ,一个潜藏很久(至少从 .NET Core 2.1 到 3.0 Preview 4)的巨坑。

这个诡异问题的谜底在偶然间终于被解开了,这时又产生了新的疑问 —— corefx 中是如何修复这个巨坑 bug 的?

查看 corefx 中与 System.Data.SqlClient 相关的 git commits ,目测发现下面的 commit (对应的 PR ),也许是这个 commit 修复的,接下来找时间验证一下。

转载于:https://www.cnblogs.com/dudu/p/10848435.html

<think>好的,我现在需要解决用户提到的System.Data.SqlClient.SqlException异常的问题。首先,我得理解这个异常通常是在什么情况下发生的。根据经验,SqlException通常与数据库连接查询有关。可能的原因包括连接字符串错误、网络问题、SQL语句语法错误、权限问题,或者数据库服务器本身的问题。 接下来,用户提到在MongoDB的操作中出现了OperationCanceled异常,然后调用了RetryableReadContext.Dispose(),但问题是想解决SqlException。可能需要先确认这两个异常是否有关联,但可能用户是在不同场景下到的问题,现在主要聚焦在SqlClient的异常上。 首先,我应该列举常见的原因,比如连接字符串是否正确,服务器是否可达,凭据是否正确,防火墙设置,SQL语句是否有语法错误,参数是否正确,以及可能的超时问题。然后,针对每个原因给出解决方案,比如检查连接字符串的参数,测试网络连接,验证登录信息,检查SQL语法,处理超时设置,以及处理资源限制如连接问题。 另外,用户可能需要知道如何捕获和处理这个异常,比如在C#中使用try-catch块,并记录异常详细信息,这有助于诊断问题。还可以提到使用SQL Server Profiler或扩展事件来跟踪查询执行情况,或者检查SQL Server的错误日志。 需要确保回答结构清晰,分点列出原因和对应的解决方案,让用户能逐步排查问题。同时,要避免使用Markdown格式,保持自然的中文表达,并添加相关的引用标识。最后生成相关问题,帮助用户深入理解可能的相关问题。</think>关于System.Data.SqlClient.SqlException异常,以下是常见原因及解决方案: ### 一、异常主要原因分析 1. **数据库连接失败** - 检查连接字符串参数是否正确,包括$ServerName$, $DatabaseName$, $UserID$, $Password$等[^1] - 验证网络连通性:使用$telnet <服务器IP> 1433$测试端口可达性 - 检查SQL Server服务是否启动:`SQL Server (MSSQLSERVER)`服务状态 2. **权限问题** - 确认登录账号具有对应数据库的$CONNECT$, $SELECT$, $INSERT$等权限 - 检查是否启用混合认证模式(SQL Server身份验证+Windows身份验证) 3. **SQL语句错误** ```csharp // 错误示例:缺少参数化查询 string sql = "SELECT * FROM Users WHERE Name='" + txtName.Text + "'"; // SQL注入风险 ``` - 应使用参数化查询: ```csharp using (SqlCommand cmd = new SqlCommand("SELECT * FROM Users WHERE Name=@Name", connection)) { cmd.Parameters.AddWithValue("@Name", txtName.Text); } ``` 4. **资源限制** - 连接池溢出:检查是否未正确关闭连接,建议使用$using$语句块 ```csharp using (SqlConnection conn = new SqlConnection(connStr)) { // 操作代码 } // 自动释放连接 ``` ### 二、诊断步骤 1. 捕获完整异常信息: ```csharp try { // 数据库操作 } catch (SqlException ex) { Console.WriteLine($"错误代码:{ex.Number}"); Console.WriteLine($"错误信息:{ex.Message}"); Console.WriteLine($"行号:{ex.LineNumber}"); Console.WriteLine($"存储过程:{ex.Procedure}"); } ``` 2. 常见错误代码对照: - 4060:数据库不存在/不可访问 - 18456:登录失败 - 547:外键约束冲突 - 2627:唯一键冲突 ### 三、优化建议 1. 设置合理超时时间: ```csharp SqlCommand cmd = new SqlCommand(query, conn); cmd.CommandTimeout = 30; // 单位:秒 ``` 2. 启用连接池配置: ```xml <connectionStrings> <add name="MyDB" connectionString="Data Source=.;Initial Catalog=DB;Max Pool Size=200;Min Pool Size=20" providerName="System.Data.SqlClient"/> </connectionStrings> ``` 3. 使用SQL Server Profiler捕获实际执行的SQL语句
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值