更新:这个问题是 System.Data.SqlClient 的一个 bug 引起的,详见 坑暗花明:又遇 .NET Core 中 System.Data.SqlClient 查询缓慢的问题

最近遇到一个非常奇特的问题,在一个 ASP.NET Core 项目中从 SQL Server 2008 R2 中查询获取 100 条记录竟然耗时 10 多秒,如果是查询本身慢,那到不是什么奇特的问题。

说它非常奇特是因为耗时主要发生在 SqlDataReader 读取数据时

2019-04-04 21:31:58.546 [Information] Executed DbCommand ("2,656"ms)
...
2019-04-04 21:32:10.690 [Debug] A data reader was disposed.

进一步测试发现

查询获取 1 条数据库记录,耗时在 230ms 左右
查询获取 10 条数据库记录,耗时在 1.6s-2s 之间
查询获取 100 条数据库记录,耗时在 12s-22s 之间

开始怀疑是 EF Core 的问题,通过在 EF Core 源码中打点,定位到耗时发生在 _dataReader.ReadAsync 处

while (await _dataReader.ReadAsync(cancellationToken))
{
_buffer.Enqueue(_valueBufferFactory.Create(_dbDataReader));
}

_dataReader.ReadAsync 实际调用的是 System.Data.SqlClient 中的 SqlDataReader.ReadAsync 方法。

一次 ReadAsync 读取一行记录,通过在 SqlClient 的源代码中打点记录时间戳发现,在 100 次一行一行读取中,其中有几次读取会出现延迟,比如某一次 13 秒延迟,100 次读取中出现了 5 次读取延迟 —— 2s + 3s + 3s + 2s + 3s = 13s 。

经过在 System.Data.SqlClient 源代码中无数次打点记录时间戳最终定位到延迟发生在  SNIPacket.ReadFromStreamAsync()  方法中  stream.ReadAsync()  时

Console.WriteLine($"Entering stream.ReadAsync() at {DateTime.Now}");
stream.ReadAsync(_data, , _capacity, CancellationToken.None).ContinueWith(t =>
{
Console.WriteLine($"stream.ReadAsync().ContinueWith at {DateTime.Now}");
//...
}

stream 对应的是 NetworkStream ,延迟发生在网络传输过程中,与 SqlClient 没关系。

TCP 抓包发现 SQL Server 服务器发送的数据到达就延迟了。

于是只能将怀疑对象锁定在 SQL Server 数据库层面。

对应的 SQL 查询语句涉及 4 张表,FROM 一张表(表A), JOIN 三张表(LEFT JOIN 表B,LEFT JOIN 表C ,INNER JOIN 表D),表A有1000多万条记录,表C有1000多万条记录,查询时按表A的主键排序,表A的聚集索引建在时间字段上,没有建在主键上。

SELECT ...
FROM TableA
LEFT JOIN TableB ON [TableA].[Id] = [TableB].[EntryID]
LEFT JOIN TableC ON [TableA].[Id] = [TableC].[EntryID]
INNER JOIN TableD ON [TableA].[BlogID] = [TableD].[BlogID]
WHERE ([TableA].[Id] >= @__startId_0)

并不是所有查询都出现这个问题,当 @__startId_0 小于一定值时会出现。

后来尝试将  LEFT JOIN TableC 改为 INNER JOIN TableC ,问题竟然消失了,但进一步测试发现当  @__startId_0  再小到一定值问题又会出现。

既然问题与 JOIN TableC 有关,那干脆不进行 JOIN ,单独查询 TableC ,然后将在 C# 代码中将查询的结果合并进行,这样改进了,查询获取 100 条记录只需 200 多毫秒。

这个奇特的问题就这样用一个简单粗暴有效的方法临时解决了。

对于这个问题的根本原因,怀疑与 TableA 没有把聚集索引建在 Id 字段上有关,但目前没法修改聚集索引进行验证,以后再找机会验证。

下单快发货慢:一个 JOIN SQL 引起 SqlClient 读取数据慢的奇特问题的更多相关文章

  1. sql server 随机读取数据

    --sql server 随机读取数据 * FROM [tablename] ORDER BY NEWID() pk from [tablename] ORDER BY NEWID()) --这两个方 ...

  2. SQL Server数据库读取数据的DateReader类及其相关类

    之前学了几天的SQL Server,现在用C#代码连接数据库了. 需要使用C#代码连接数据库,读取数据. 涉及的类有: ConfigurationManage SqlConnection SqlCom ...

  3. 漫谈可视化Prefuse(一)---从SQL Server数据库读取数据

    上篇<可视化工具solo show-----Prefuse自带例子GraphView讲解>主要介绍了整个Prefuse工具集具有的一些特征.框架的运行流程,分析并展现了官方提供的例子Gra ...

  4. 编写程序,将来自文件中的行保存在一个vector<string>,然后使用一个istringstream 从vector中读取数据,每次读一个单词

    #include<fstream> #include <vector> #include<string> #include<iostream> #inc ...

  5. ASP.NET用SQL Server中的数据来生成JSON字符串

    原文引自:  作者: 缺水的海豚  来源: 博客园  发布时间: 2010-09-21 21:47  阅读: 6136 次  推荐: 0   原文链接   [收藏] 摘要:ExtJs用到的数据内容基本 ...

  6. web手工项目03-登录功能测试用例及缺陷编写-流程图画法-前后台下单及发货流程图-流程图设计测试用例方法-功能测试涉及到的四种数据库场景

    回顾 注册功能测试(步骤,需求分析(输入分析,处理分析,输出分析),数据构造(有效等价类,无效等价类,有效数据,无效数据),编写用例,执行用例,缺陷报告) 轮播图功能测试(步骤,需求分析拆分测试点,测 ...

  7. [转]一个用户SQL慢查询分析,原因及优化

    来源:http://blog.rds.aliyun.com/2014/05/23/%E4%B8%80%E4%B8%AA%E7%94%A8%E6%88%B7sql%E6%85%A2%E6%9F%A5%E ...

  8. 一个有趣的SQL Server 层级汇总数据问题

        看SQL Server大V宋大侠的博客文章,发现了一个有趣的sql server层级汇总数据问题.          具体的问题如下:     parent_id emp_id emp_nam ...

  9. 一个神奇SQL引发的故障【转】

    前几天一个客户数据库主实例告警,诊断过程中发现是由一个慢SQL导致的数据库故障,而在排查逐步深入之后却发现这个现象的不可思议. 问题描述 2016年12日09日,大概9点26分左右,一个客户的生产库主 ...

随机推荐

  1. jmeter 连接 MySql

    1.连接 mysql 驱动包安装下载 1)首先需要安装 mysql 驱动包,下载地址:https://dev.mysql.com/downloads/connector/j/5.1.html 2)下载 ...

  2. 分布式版本控制系统Git的安装和使用

    作业要求来自https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/2097 GitHub远程仓库的地址:https://github.com/ ...

  3. Android 查阅博客2_APT

    https://mp.weixin.qq.com/s/3zrAzOUGpovRRbuYnce3uw APT(Annotation Processing Tool) 即注解处理器,是一种注解处理工具,用 ...

  4. UGUI中Text的换行

    通过代码中的\n可以直接执行换行效果,但是我们在平常的工作中一般都是读表,既在Inspector面板中的Text组件中输入同样的内容就达不到换行效果: 其实unity把\n转变成了\\n,我们只需要变 ...

  5. Python自动化测试用例设计--自动化测试用例与手工测试用例区别与联系

    1. 前言 手工测试用例是针对手工测试人员,自动化测试用例是针对自动化测试框架,前者是手工测试用例人员应用手工方式进行用例解析,后者是应用脚本技术进行用例解析,两者最大的各自特点在于,前者具有较好的异 ...

  6. jq修改导航栏样式(选中、使用两张图片替代的是否选中效果)

    <footer class="toolbar"> <ul> <li> <a href="{:url('Index/home')} ...

  7. IIS上配置单页面404

    问题 因为我们的应用是单页客户端应用,当用户在浏览器直接访问http://www.xxx.com/user时,刷新页面的时候,会返回404错误. 问题原因 服务端URL匹配不到相应的路由资源 解决方案 ...

  8. PHP pthread 多线程 案例

    <?php /** *检测http服务是否可以正常访问 *启动三个线程执行任务 */ class taskWork extends Thread { public $url = ''; //检测 ...

  9. idea本地运行JavaWeb项目

    1.需安装的软件有: JDK,当前版本jdk1.8 maven,当前版本3.2.1 mysql,mysql5.7 tomcat,tomcat9 git客户端,TortoiseGit-2.1.0.0-6 ...

  10. pytbull 手册

    - Official documentation for pytbull v2.1 - Table of content Description Architecture Remote mode Lo ...