【问题标题】:EXTREMELY Poor LINQ Query Performance When Using Skip/Take for Paging使用 Skip/Take 进行分页时 LINQ 查询性能极差
【发布时间】:2013-11-26 04:55:16
【问题描述】:

我需要使用 LINQ 从 DB2 数据库中查询记录。我有从 DB 模式生成的实体,并且正在尝试使用 Skip 和 Take 执行 LINQ 查询。基础表有 25 列,可能有一百万条记录。当我在没有“Skip()”的情况下执行查询时,大约需要 0.508 毫秒才能完成。当我包含 Skip() 时,它需要将近 30 秒。大不同。

谁能告诉我为什么会这样?

更新:这是我正在使用的 LINQ 查询。

var x = 30;

var results = context.ASSET_T
.OrderBy(c => c.ASSET_ID)
.Skip(x)
.Take(x)
.ToList();

更新:所以我只是尝试更新查询,以便我只返回一个列,ASSET_ID。当我只返回那一列时,带有 Skip() 的查询只需要 0.256 毫秒。

var x = 30;

var results = context.ASSET_T
.OrderBy(c => c.ASSET_ID)
.Skip(x)
.Take(x)
.Select(c => c.ASSET_ID)
.ToList();

如果我包含任何其他列,则查询执行时间会增加戏剧性地

例如下面的查询需要 10 秒来执行。

var x = 30;

var results = context.ASSET_T
.OrderBy(c => c.ASSET_ID)
.Skip(x)
.Take(x)
.Select(c => new {
                 ASSET_ID = c.ASSET_ID,
                 ASSET_TYP = c.ASSET_TYP
                 ASSET_DESC = c.ASSET_DESC
                 })
.ToList();

更新:我现在发现我尝试查询的表中的列存在问题(可能与索引相关)。正如我上面提到的,当我执行一个只返回 ASSET_ID 列的查询时,它只需要 0.256 毫秒。如果我尝试执行 ONLY 返回 ASSET_DESC 的查询或 ONLY 返回 ASSET_TYP 的查询,则查询执行时间跳到 9 秒左右。

这是否表明其他列当前没有被索引?

更新:我已经添加了上述 LINQ 查询的 SQL 输出。

SELECT 
Project1.C1 AS C1, 
Project1.ASSET_ID AS ASSET_ID, 
Project1.ASSET_TYP AS ASSET_TYP, 
Project1.ASSET_DESC AS ASSET_DESC
FROM ( SELECT Project1.ASSET_ID AS ASSET_ID, Project1.ASSET_TYP AS ASSET_TYP, Project1.ASSET_DESC AS ASSET_DESC, Project1.C1 AS C1, row_number() OVER (ORDER BY Project1.ASSET_ID ASC, Project1.ASSET_TYP ASC, Project1.ASSET_DESC ASC) AS row_number
  FROM ( SELECT 
    Extent1.ASSET_ID AS ASSET_ID, 
    Extent1.ASSET_TYP AS ASSET_TYP, 
    Extent1.ASSET_DESC AS ASSET_DESC, 
    CAST(1 AS int) AS C1
    FROM MYDB.ASSET_T AS Extent1
  )  AS Project1
)  AS Project1
WHERE Project1.row_number > 1
ORDER BY Project1.ASSET_ID ASC, Project1.ASSET_TYP ASC, Project1.ASSET_DESC ASC FETCH  FIRST 31 ROWS ONLY 

【问题讨论】:

  • 生成的SQL是什么?
  • 显示您编写的 LINQ 查询。
  • 查询是否曾经被视为IEnumerable<T>?还是一直是IQueryable<T>
  • 虽然我没有看到任何突然出现的东西,但您可能需要阅读 msdn.microsoft.com/en-us/data/hh949853 的性能注意事项。有一节介绍如何解决性能问题。
  • 使用Skip完全改变sql查询是可以理解的,而只使用Take可能会导致使用Top的sql查询。

标签: c# linq linq-to-entities paging query-performance


【解决方案1】:

您是否查看过为此查询生成的 SQL?

据我所知,Skip() Take() 最终会生成一个使用名为 Row_Number() 的函数的语句。此函数以如下所示的方式在整个记录集中执行 - 在获取所需的开始值和结束值之间的值之前,将行号作为结果中的第一个生成列插入,通常会使其非常慢,在大型记录上套..

SELECT ...
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t0].[...]) AS [ROW_NUMBER], ... ,
    FROM [table] AS [t0]
    ) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
ORDER BY [t1].[ROW_NUMBER]

如果您可以使用索引数字列并对其进行排列,以便您自己阅读 >= start_value AND

我有很好的索引数据库,有 1 亿条记录,Skip().Take() 最多需要 30 分钟才能获得 25 条记录。在哪里直接读取大约需要 20-40ms。

这意味着您必须考虑实现分页的编码方式,并且在您的情况下实施可能不切实际。

【讨论】:

    【解决方案2】:

    我猜这不是由于 linq,尝试先按索引列排序,然后跳过。 在您的第一种情况下,当您只是采取时,它不会遍历其余记录而只会选择最佳结果,而跳过它需要对它们进行排序以找出顺序,然后根据它进行跳过。

    【讨论】:

    • 对。我理解为什么添加 Skip 会使查询花费更长的时间来执行,因为它必须通过其余的记录来对它们进行排序/排序。我确实在 Skip/Take 之前使用了 OrderBy,并且我相信我订购的列已编入索引。
    • @Konstantin - 这个答案节省了我很多时间。在列中添加 NC 索引减少了前一次的 75%。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 2010-10-20
    • 2011-04-09
    • 2014-02-11
    • 2023-01-19
    • 2013-11-25
    相关资源
    最近更新 更多