当前位置: 首页 > news >正文

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

更新:这个问题是 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, 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

相关文章:

  • ES6之路之模块详解
  • [十二省联考]字符串问题
  • FFmpeg 硬件加速方案概览 (下)
  • Vuex.js状态管理共享数据 - day8
  • 量子计算可以给企业竞争带来的七种优势
  • IT兄弟连 JavaWeb教程 Servlet线程安全问题
  • laravel5.5 视图共享数据
  • 小猿圈网站页面底部固定的方法
  • mysqlclient操作MySQL关系型数据库
  • Loadrunner报Failed to connect to server 127.0.0.1
  • poj1284(欧拉函数+原根)
  • 应用中有使用到集群么?多大规模?
  • Azure RIS的工作原理以及其与AWS RIs的比较
  • 使用DB业务拆分解决写压力大问题
  • JS中的值比较操作:==,===,Object.is()
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • “大数据应用场景”之隔壁老王(连载四)
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • Android组件 - 收藏集 - 掘金
  • Angular2开发踩坑系列-生产环境编译
  • C学习-枚举(九)
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • ES6--对象的扩展
  • Mysql5.6主从复制
  • PHP 小技巧
  • PHP变量
  • React-生命周期杂记
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • 记一次删除Git记录中的大文件的过程
  • 七牛云假注销小指南
  • 数据科学 第 3 章 11 字符串处理
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • Mac 上flink的安装与启动
  • 带你开发类似Pokemon Go的AR游戏
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • # Apache SeaTunnel 究竟是什么?
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (JS基础)String 类型
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (windows2012共享文件夹和防火墙设置
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (附源码)计算机毕业设计大学生兼职系统
  • (附源码)计算机毕业设计高校学生选课系统
  • (篇九)MySQL常用内置函数
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (转)大道至简,职场上做人做事做管理
  • (转)关于pipe()的详细解析
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .Net core 6.0 升8.0