了解查询计划更改的原因

Nie*_*jes 5 sql-server optimization execution-plan

我们的 ERP 系统存在一些性能问题。06:00 左右,问题开始出现。长话短说; 我的一位同事执行了DBCC FREEPROCCACHE(我知道这不是首选操作,但这不是重点)。之后,他重新启动了整个 ERP 应用程序,而不是数据库引擎。这似乎没有帮助。我查看了查询存储,发现查询计划在 06:05 左右发生了更改。我尝试强制执行旧计划,性能问题就消失了。

我比较了 2 个计划 XML,发现两者都基于相同的统计数据(更新日期完全相同)。索引上的更改数量不同,但这不会触发更新统计信息,因为否则新的查询计划将为所使用的统计信息提供新的日期。我检查了涉及的表的所有统计信息,但自上次维护工作以来,它们没有更新。最后一次维护作业在本周末运行,这也是两个查询计划中使用的统计信息的更新日期。

该查询是参数化查询,所以我有点不清楚为什么查询计划突然改变。我认为恰恰相反,查询突然运行缓慢,因为相同的计划用于不同的参数。这里的情况似乎并非如此,因为我强制使用旧的查询计划。

该查询来自一些检索订单的定制工作。现在,我怀疑有人发出了参数化查询,该查询会返回一个非常大的数据集,从而减慢 ERP 系统的速度。然后,我的同事运行了一个DBCC FREEPROCCACHE命令,根据本周末的新统计数据触发了查询计划的重新编译。当前查询继续运行,他决定重新启动 ERP 应用程序。现在,使用新的查询计划,所有新查询都会变慢,直到我强制使用旧的查询计划。

这能是解释吗?我有点怀疑,因为当统计数据发生变化时,SQL Server会触发重新编译,所以计划应该在索引维护作业之后已经重新编译。

我想我在这里遗漏了一些东西,但我不确定是什么。它是SQL Server 2016 Standard,是ERP系统的专用服务器。

编辑 15-4-2020 12:17:既然我写了这个...我认为问题在于DBCC FREEPROCCACHE第一次执行有问题的参数化查询的参数使用了不同的参数,这导致 SQL Server 重新编译(坏)计划。或者我还缺少什么吗?

Con*_*SFT 5

统计信息是查询优化器的一种输入,但不是唯一的输入。正如文章中提到的,优化器还在编译期间嗅探参数值,并使用这些值通过使用当时存在的统计信息来生成对各种操作(例如过滤器、连接和分组)产生的行数的估计。汇编。此外,存储引擎为每个编译提供行计数,并且这也可以在不更改统计信息的情况下进行更改(尽管如果更改足够多的行,优化器最终会强制重新计算统计信息)。这些事情中的任何变化都可能导致计划从一个编译更改为下一个编译。查询存储实际上还跟踪其他因素,它在内部使用这些因素通过名为 sys.context_settings 的表向您表示查询在语义上是相同还是不同。因此,ansi null 开/关就是这样一种语义变化,也可能导致结果差异。查询存储从您那里抽象出这些,但是如果您要在存储过程中创建一个循环来更改 ansi null 开/关,然后在循环中一遍又一遍地运行相同的语句,您实际上会得到一遍又一遍地编译(每次从上次编译存储过程中该部分的语句开始更改设置),这也让您有机会在每次执行时嗅探新参数。从技术上讲,它们在查询存储中显示为不同的查询,但对于 SQL 引擎来说,它们在存储过程的缓存查询计划中保留相同的语句槽。

SQL 2017+ 包含一项称为“自动调优”的功能,该功能可以自动强制执行旧计划以进行大型回归。Azure SQL DB 也具有相同的功能。将来,我们希望让参数嗅探不再成为客户的问题,因此我希望这能让您同时了解如何解决您的场景中的问题。

  • 我们在编译时查看行计数。某些功能(例如内存授予反馈)会间接查看每次执行信息以纠正潜在的基数错误,因为它们与后续编译执行的内存授予预留相关。自适应(批处理模式)连接和交错执行可以以更积极的方式查看通常严重影响性能的模式的基数。然而,一般的答案是仅在编译期间查看行计数。 (2认同)
  • 有一个行修改计数器快照到统计信息中,以跟踪稍后确定统计信息是否过时的时间。存储引擎在编译期间提供行计数器,而不是统计数据,以确定表扫描的预期行计数。优化器将两者结合起来以确定不同运算符中的基数估计 (2认同)