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

  • 时间:
  • 浏览:0
  • 来源:幸运快3_快3充值_幸运快3充值

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

最近遇到一有一两个 非常奇特的大大问题,在一有一两个 ASP.NET Core 项目中从 SQL Server 4008 R2 中查询获取 400 条记录竟然耗时 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 条数据库记录,耗时在 2400ms 左右  
查询获取 10 条数据库记录,耗时在 1.6s-2s 之间
查询获取 400 条数据库记录,耗时在 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 的源代码中打点记录时间戳发现,在 400 次一行一行读取中,其包含多少读取会跳出延迟,比如某一次 13 秒延迟,400 次读取中跳出了 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有4000多万条记录,表C有4000多万条记录,查询时按表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# 代码中将查询的结果合并进行,另有一两个 改进了,查询获取 400 条记录只需 400 多毫秒。

你这个 奇特的大大问题就另有一两个 用一有一两个 简单粗暴有效的法律办法临时处置了。

对于你这个 大大问题的根本导致 ,怀疑与 TableA 没法 把聚集索引建在 Id 字段上有关,但目前没法 修改聚集索引进行验证,完后 再找或者验证。