下单快发货慢:一个 JOIN SQL 引起 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, 0, _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 字段上有关,但目前没法修改聚集索引进行验证,以后再找机会验证。

原文地址:https://www.cnblogs.com/dudu/p/10792231.html

时间: 2024-08-29 22:25:42

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

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

上篇<可视化工具solo show-----Prefuse自带例子GraphView讲解>主要介绍了整个Prefuse工具集具有的一些特征.框架的运行流程,分析并展现了官方提供的例子GraphView.java. 这几天相继的看了prefuse.data.prefuse.data.expression等包的具体接口,大致了解了prefuse框架是如何完成外部数据与prefuse数据之间的映射关系转换:如何通过prefuse.data.expression包中的各个类完成对于逻辑表达式.字符串表

一个实用的从文本文件读取数据进行排序的程序

程序说明 这是一个十分可靠的程序,这个程序的查错能力非常强悍.程序包含了文件操作,归并排序和字符串输入等多种技术. 程序的功能是从外部读取一个包括int型数据的文本文件,然后将它保存到内部临时数组,对数组进行排序后,以文本形式输出到指定的文件上.因为是int类型的数据,没有很严重的损失精度的问题. 正常运行要求: 包括数据的源文件内不能包括其他任何除数字和空白字符(空格,制表符,换行符)之外的任何字符,源文件最开始必须是数字字符,要保证源文件的数据计数正确.同时保证文件名有效. 完整代码 警告:

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

原文引自:  作者: 缺水的海豚  来源: 博客园  发布时间: 2010-09-21 21:47  阅读: 6136 次  推荐: 0   原文链接   [收藏] 摘要:ExtJs用到的数据内容基本上都是JSON格式的,要和ASP.NET结合开发,需要格式化数据成JSON的东西.所以作者实现了一个生成JSON字符串的方法. 最近在学习ExtJs与ASP.NET结合开发,前者用到的数据内容基本上都是JSON格式,遂想写一个格式化数据成JSON的东东,在网上找了一下,发现还蛮多的,实现了一个类似于

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

看SQL Server大V宋大侠的博客文章,发现了一个有趣的sql server层级汇总数据问题. 具体的问题如下: parent_id emp_id emp_name total_amout     NULL 2 Andrew 200     2 1 Nancy 100     2 3 Janet 120     3 4 Michael 80     1 5 Robert 50     每个员工的总销售额=自己的销售额+其下级员工的总销售额,     比如:     Andrew = 200_

[转]一个用户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%E8%AF%A2%E5%88%86%E6%9E%90%EF%BC%8C%E5%8E%9F%E5%9B%A0%E5%8F%8A%E4%BC%98%E5%8C%96/ 问题描述 一个用户反映先线一个SQL语句执行时间慢得无法接受.SQL语句看上去很简单(本文描述中修改了表名和字段名):SELECT cou

SQL 快速新增权限数据表(使用cross join)

摘要:SQL 快速新增权限数据表(使用cross join) 1.因为设计系统的时候 将角色 权限 系统都做了分开处理 偏偏交集起来相当多数据 2.决定不自己打 使用cross join 先练习一下一般的语法 --use 数据库 --select a.浏览,a.修改,a.删除,b.系统名称,c.角色名称 --from 角色权限 as a --cross join 系统 as b --cross join 角色 as c --go 像这样 3.开始改写 使用cross join --先清空表单 已

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

前几天一个客户数据库主实例告警,诊断过程中发现是由一个慢SQL导致的数据库故障,而在排查逐步深入之后却发现这个现象的不可思议. 问题描述 2016年12日09日,大概9点26分左右,一个客户的生产库主实例发出告警,告警信息如下: MySQL实例超过五分钟没有更新.这个告警信息简单解释下就是持续五分钟无法获取该实例的信息. 同时开发人员还反映,从12月09日凌晨1点开始,已经出现一些数据库请求超时的现象,直到出现告警信息之后业务恢复正常. 问题排查 1.监控进程排查 该数据库系统使用袋鼠云Easy

如何快速写出一个陌生人推荐系统

如何快速写出一个陌生人推荐系统 在社交游戏中,除了和好友互动,经常还会设计陌生人互动的游戏环节.下面两张图分别是QQ水浒和全民农场的陌生人推荐界面. QQ水浒陌生人界面 全民农场陌生人界面 那么,陌生人推荐系统一般是怎么做的呢?下面以全民农场的陌生人推荐系统为例来阐述如何快速构建一个陌生人推荐系统,由于采用了boost::multi_index库,整个推荐系统代码在400行左右,非常简洁. 首先,我们简单介绍一下全民农场的陌生人推荐系统规则: 1. 等级相近 2. 城市相近 3. 性别相反 4.

sql server exec 查询数据,将查询出来的数据赋值给一个定义的变量

sql server exec 查询数据,将查询出来的数据赋值给一个定义的变量 declare @num int, @sqlnum nvarchar(500) set @sqlnum=N''; set @sqlnum =N'select @a=max(SINGLE_CORE_COUNT) from SFC_MODULE_STACKING_DATA where '+@strsql; exec sp_executesql @sqlnum,N'@a int output',@a=@num output