更新:这个问题是 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. Jenkins部署报weblogic.deploy.api.tools.deployer.DeployerException: Java heap space

    Jenkins部署报weblogic.deploy.api.tools.deployer.DeployerException: Java heap space异常 解决办法: 在MAVEN_OPTS中 ...

  2. 201772020113李清华《面向对象程序设计(java)》第八周学习总结

    实验六 接口的定义与使用 实验时间 2018-10-18 1.实验目的与要求 (1) 掌握接口定义方法: (2) 掌握实现接口类的定义要求: (3) 掌握实现了接口类的使用要求: (4) 掌握程序回调 ...

  3. leetcode347

    public class Solution { public IList<int> TopKFrequent(int[] nums, int k) { var dic = new Dict ...

  4. js 模拟css3 动画2

    <html> <head> <title> javaScript缓动入门 </title> </head> <body> < ...

  5. 学习node.js 第2篇 介绍node.js 安装

    Node.js - 环境安装配置 如果愿意安装设置Node.js环境,需要计算机上提供以下两个软件: 一.文本编辑器 二.Node.js二进制安装包 文本编辑器 这将用来编写程序代码. 一些编辑器包括 ...

  6. Smart Contract - Hello World

    [编写Smart Contract] 1.包含头文件. #include <eosiolib/eosio.hpp> #include <eosiolib/print.hpp> ...

  7. python 安装包制作

    1. __init__.py 2.模块1 模块2 3.setup.py from distutils.core import setup setup(name='modules_name',versi ...

  8. Step by Step Guide on Yanhua ACDP Clear BMW EGS ISN

    Yanhua Mini ACDP authorize new function on BMW EGS ISN clearing.So here UOBDII want to share this st ...

  9. OSI网络七层协议+火了火了火

    因为部门新进了一台价值百万的网络测试设备,所以有太大的必要了解有关网络相关的基础知识了. 网络七层协议OSI(open system interconnection)从上到下依次为:应用层.表示层.会 ...

  10. 注解@ResponseBody的作用

    @ResponseBody通常是放在方法上,主要是在前端页面异步请求的时候,返回数据使用.直白点说就是加上这个注解之后,return的数据不会解析成返回跳转路径,而是会默认放在  response b ...