OPTION FORCE ORDER 提高性能,直到行被删除

sha*_*non 10 performance sql-server-2008 sql-server optimization hints query-performance

我有一个有点复杂的 SQL Server 2008 查询(大约 200 行相当密集的 SQL),它没有按照我的需要执行。随着时间的推移,性能从大约 0.5 秒下降到大约 2 秒。

查看执行计划,很明显,通过重新排序连接,可以提高性能。我做到了,它做到了……下降到大约 0.3 秒。现在查询有“OPTION FORCE ORDER”提示,生活很好。

今天我来了,清理数据库。我归档了大约 20% 的行,除了删除行之外,在相关数据库中没有采取任何行动……执行计划完全被控制。它完全错误地判断了某些子树将返回多少行,并且(例如)替换了一个:

<Hash>
Run Code Online (Sandbox Code Playgroud)

<NestedLoops Optimized='false' WithUnorderedPrefetch='true'>
Run Code Online (Sandbox Code Playgroud)

现在查询时间从大约 0.3 秒飙升到大约 18 秒。(!) 只是因为我删除了行。如果我删除查询提示,我将回到大约 2 秒的查询时间。更好,但更糟。

将数据库恢复到多个位置和服务器后,我重现了该问题。简单地从每个表中删除大约 20% 的行总是会导致这个问题。

  1. 对于强制连接顺序使查询估计完全不准确(因此查询时间不可预测),这是否正常?
  2. 我应该只是期望我要么接受次优查询性能,要么像鹰一样观察它并经常手动编辑查询提示?或者也提示每个加入?.3s 到 2s 是一个很大的打击。
  3. 为什么优化器在删除行后爆炸很明显?例如,“是的,它进行了样本扫描,并且由于我在数据历史记录中较早地存档了大部分行,样本产生了稀疏结果,因此它低估了对排序散列操作的需求”?

如果您想查看执行计划,请建议我可以发布它们的位置。否则,我已经采样了最令人惊叹的一点。这是基本的错误估计,括号中的数字是(估计:实际)行。

                             /  Clustered Index Scan (908:7229)
Nested Loops (Inner Join) --<
                             \  NonClustered Index Seek (1:7229)
Run Code Online (Sandbox Code Playgroud)

请注意,内循环预期扫描 908 行,但扫描 52,258,441。如果它是准确的,这个分支会运行大约 2 毫秒,而不是 12 秒。在删除行之前,这个内部连接估计值仅相差 2 倍,并且作为两个聚集索引的哈希匹配执行。

Mar*_*ith 7

对于强制连接顺序使查询估计完全不准确(因此查询时间不可预测),这是否正常?

FORCE ORDER 的使用不会使估计不准确,行的删除确实如此。强制更新表上的统计信息可能会提高估计的准确性。

我应该只是期望我要么接受次优查询性能,要么像鹰一样观察它并经常手动编辑查询提示?或者也提示每个加入?.3s 到 2s 是一个很大的打击。

最好是确保优化器获得生成最佳计划所需的信息,而不使用 FORCE ORDER 提示。通过这样做,它应该更好地应对底层数据分布的变化,而无需人工干预。也就是说,如果数据的性质使得基数可能逐小时或逐小时显着变化,请考虑使用计划指南来确保计划是固定的。

为什么优化器在删除行后爆炸很明显?例如,“是的,它进行了样本扫描,并且由于我在数据历史记录中较早地存档了大部分行,样本产生了稀疏结果,因此它低估了对排序散列操作的需求”?

您没有提到问题表中的行数,但删除很可能是:

  • 没有删除足够的行来触发统计更新。当 20% 的行已被修改但可以选择使用跟踪标志 2371来启用动态阈值时,应该会发生这种情况。
  • 确实触发了统计更新,但收集的样本不具有代表性。通过使用 FULLSCAN运行手动更新来更正此问题。

您也可能会遇到老式的参数嗅探问题,对此有无数的选择可以解决。WITH RECOMPILE可能是使用如此大的查询指定的昂贵选项,但在过程和语句级别都值得研究。