【问题标题】:SQL Server Query Optimisation - Unexpected slowness in a simple querySQL Server 查询优化 - 简单查询中的意外缓慢
【发布时间】:2016-12-31 11:30:13
【问题描述】:

可能的解释在这里in the comment

在 SQL Server 2014 企业版(64 位)中 - 我正在尝试从视图中读取。标准查询只包含一个 ORDER BYOFFSET-FETCH 子句,如下所示。

方法 1

SELECT

    *

FROM Metadata
ORDER BY
    AgeInHours      ASC, 
    RankingPoint    DESC, 
    PublishDate     DESC
OFFSET 150000 ROWS
FETCH NEXT 40 ROWS ONLY

但是,这个相当简单的查询的执行速度几乎比返回相同结果的以下查询慢 9 倍(在跳过像 150k 这样的大量行时很明显)。

在这种情况下,我首先读取主键,然后将其用作WHERE...IN 函数的参数

方法 2

SELECT 
    * 
FROM Metadata
WHERE NewsId IN (
    SELECT

        NewsId

    FROM Metadata
    ORDER BY
        AgeInHours      ASC, 
        RankingPoint    DESC, 
        PublishDate     DESC
    OFFSET 150000 ROWS
    FETCH NEXT 40 ROWS ONLY
)
ORDER BY
    AgeInHours      ASC, 
    RankingPoint    DESC, 
    PublishDate     DESC

对这两者进行基准测试显示了这种差异

(40 row(s) affected)

 SQL Server Execution Times:
   CPU time = 14748 ms,  elapsed time = 3329 ms.

(40 row(s) affected)

 SQL Server Execution Times:
   CPU time = 3828 ms,  elapsed time = 469 ms.

我在主键PubilshDate 上有索引,它们的碎片非常低。我也尝试对数据库表运行类似的查询,但在每种情况下,第二种方法都会产生巨大的性能提升。我还在 SQL Server 2012 上对此进行了测试。

有人能解释一下是怎么回事吗?

架构

方法 1:执行计划

方法二:执行计划(左)

方法二:执行计划(右图)

【问题讨论】:

  • 如果您运行第一种方法并选择only ID 列,会发生什么?我怀疑* 星号会减慢查询速度,因为它会强制它扫描整个列。
  • 快得多(比方说快 9 倍),因为我认为它只查找索引列 (Id)。令人费解的是,在第二个查询中,当我使用WHERE-IN 子句时,它仍在读取相同数量的数据——但不需要时间。幕后发生了什么?
  • 能否包含查询计划和表架构?
  • @destination-data 稍后会发布,谢谢。
  • 看起来速度变慢是因为排序的数据量很大。第一种方法导致对更宽的行进行排序,其中包含 [NewsItems] 中的所有视图列,而第二种方法仅对 4 列宽的数据集进行排序

标签: sql sql-server sql-server-2014 database-performance query-performance


【解决方案1】:

当您执行查询时,引擎会查找可用于获得最佳性能的索引。您的方法 1 使用的索引不包括 SELECT 语句中的所有列,这会导致查询计划中的键查找,根据我的经验,在 SELECT 语句中仅使用索引列总是会降低性能。

如果您为AgeInHours, RankingPoint, PublishDate 创建索引并包含所有列(建议仅用于测试目的),您可以看到差异。

对于您的第二种方法,如果您使用 CTE 然后使用 JOIN 而不是使用 IN 的 WHERE 或使用索引的临时表(如果您有数百万行),您甚至可以获得更好的性能。

【讨论】:

    【解决方案2】:

    对于具有相同结果集的不同结构的查询,您会获得具有不同方法和查询成本的不同查询计划。这对于各种 SQL RDBMS 实现很常见。

    基本上在上面的示例中,从大表中选择一小部分数据是一种很好的方法,首先减少和最小化结果中的行数,然后选择包含所有列的完整行,就像您的 2. 查询一样。

    另一种方法是在第一步中建立精确的适当索引以减少结果集。在上面的查询中,可能来自 ORDER BY 子句的列在同一列中,排序顺序可能是一个解决方案。

    (你没有发送查询计划中提到的索引结构我可以想象他们的名字背后隐藏了什么。)

    您还可以使用 SQL 索引提示将 SQL 优化器引导到您认为最适合任务的特定索引,以防 SQL 优化器无法完成工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多