【问题标题】:SQL Server Hash joins and nested loopsSQL Server 哈希联接和嵌套循环
【发布时间】:2014-06-26 19:52:45
【问题描述】:

我针对需要以多种不同方式收集数据的现有数据库 (SQL2008 - SQL2012) 开发报告。

一个典型的查询可能看起来像

WITH ThisYearData AS (
    SELECT ...
)
,LastYearData AS (
    SELECT ...
)
,BudgetData AS (
    SELECT ...
)
SELECT ...
FROM SomeDateTable
     LEFT JOIN ThisYearData
         ON ...
     LEFT JOIN BudgetData
     ...

有时 CTE 相互依赖,一个典型的查询涉及其中的 5-10 个。

问题在于,在一小部分数据上运行类似的查询可能需要 1 秒到 5 分钟不等。

性能下降的主要原因是 SQL Server 使用嵌套循环而不是哈希连接。在某些情况下,我可以将HASH JOIN 放在正确的位置,以使查询速度提高 100 倍。在其他情况下,我会将一个或多个 CTE 转换为表变量。

我知道选择嵌套循环而不是散列连接的原因是基于基于统计的估计行数。 在我的情况下,我无权访问数据库架构,因此无法添加索引或统计信息,但我可以确保更新现有统计信息(并启用自动创建/更新统计信息)。

要找到导致减速的嵌套循环,我这样做:

  1. 查找具有大量行和执行的节点
  2. 沿着路径向上直到找到正确的嵌套循环
  3. 找出导致嵌套循环的连接并将其强制为hash join

SQL Sentry explorer 让这变得容易多了)

我的问题是:

  1. 我是否做了一些根本性的错误,导致 SQL Server 几乎总是低估行数(单位数估计和实际以千为单位很常见)
  2. 执行计划通常会显示一些不相关的表扫描,百分比很高,这不是很有帮助。执行计划中的百分比是多少?我可以将其更改为对我的场景更有帮助吗?

【问题讨论】:

    标签: sql-server join sql-execution-plan


    【解决方案1】:

    使用 WITH 肯定会降低性能,至少根据我的经验在 SQL Server 上是这样。在没有 WITH 结构的情况下编写查询,看看您是否获得了性能改进(我的钱是:是)。

    如果即使这样也不起作用,请在临时表中构建您原本要从 CTE 或派生表中选择的内容。仅当行数非常少(即 10 或更少)时,表变量才是最好的。如果您发现使用 CTE/派生表的查询速度太慢,至少根据我的经验,临时表可以提供最佳性能。

    【讨论】:

    • 我没有看到 CTE 或表变量有任何性能问题。如果我只是强制 HASH JOIN 性能是可以接受的(CTE 的可读性/可维护性优于其他解决方案非常有价值)
    • @adrianm 我只能根据经验说话,那就是 SQL 2005 和 2008。在大型数据集上运行的任何足够复杂的查询,如果您使用临时表在不使用 WITH 的情况下重写它,运行速度会更快。当然,如果您的查询在几秒钟内运行,那么优化它是没有用的。如果它运行几个小时,那么肯定。这不是理论,这是我从实践中学到的。我不能代表其他版本或 RDMS。可读性/可维护性确实不会受到 IMO 的影响,但这只是我的看法。
    • 只是一个指针,供您使用 CTE 的/派生表查看性能不佳的原因:查看 CTE 物化,以及为什么在某些情况下需要这样做。 CTE 没有具体化,在某些情况下,每次访问时都会重新构建。他们也没有索引或统计数据。临时表是通过设计实现的,并且可以具有索引/统计信息。您选择构建查询的内容应取决于复杂性和行数。处理小行的简单查询不需要优化。如果您使用 WITH,则很可能会针对大量行进行大规模复杂查询
    • 至少在我做过的测试中,使用表变量比使用临时表要慢。主要问题是表变量没有统计信息,而临时表有。表变量只能有一个主键(或唯一约束),而临时表可以有任意数量的索引。您可以在线查找比较... 经验法则:使用表变量进行 UDF 返回,使用临时表进行临时存储/临时数据操作,尤其是大型数据集。
    【解决方案2】:

    执行计划通常会显示一些不相关的表扫描 百分比这不是很有帮助。中的百分比是多少 执行计划基于?我可以将其更改为对我更有帮助吗 场景?

    添加提示“哈希连接”的问题在于它也会导致所有其他连接的隐式“强制顺序”。

    从这里:https://msdn.microsoft.com/en-us/library/ms173815(v=sql.105).aspx

    备注

    连接提示在查询的 FROM 子句中指定。加入提示 在两个表之间强制执行连接策略。如果加入提示是 为任意两个表指定,查询优化器自动 强制查询中所有连接表的连接顺序,基于 ON关键字的位置。

    我认为您应该复制数据库并添加统计信息以尝试提高测试环境中的性能,看看会发生什么。如果它有帮助,那么您可以与决策者交谈,或者您可以创建一个数据仓库。

    【讨论】:

      猜你喜欢
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-25
      • 1970-01-01
      • 2015-04-16
      相关资源
      最近更新 更多