202

我遇到了一个奇怪的情况,附加OPTION (RECOMPILE)到我的查询会导致它在半秒内运行,而省略它会导致查询花费超过五分钟。

当从查询分析器或我的 C# 程序通过SqlCommand.ExecuteReader(). 打电话(或不打电话)DBCC FREEPROCCACHEDBCC dropcleanbuffers没有区别;查询结果总是会在OPTION (RECOMPILE)超过五分钟后立即返回。始终使用相同的参数调用查询 [为了这个测试]。

我正在使用 SQL Server 2008。

我对编写 SQL 相当满意,但以前从未OPTION在查询中使用过命令,并且在浏览此论坛上的帖子之前对计划缓存的整个概念并不熟悉。我从帖子中了解到这OPTION (RECOMPILE)是一项昂贵的操作。它显然为查询创建了一个新的查找策略。那么,为什么省略 的后续查询OPTION (RECOMPILE)如此缓慢呢?后续查询不应该使用在先前调用中计算的包含重新编译提示的查找策略吗?

每次调用都需要重新编译提示的查询是不是很不寻常?

对不起,入门级的问题,但我真的不能做出正面或反面。

更新:我被要求发布查询...

select acctNo,min(date) earliestDate 
from( 
    select acctNo,tradeDate as date 
    from datafeed_trans 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_money 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_jnl 
    where feedid=@feedID and feedDate=@feedDate 
)t1 
group by t1.acctNo
OPTION(RECOMPILE)

从查询分析器运行测试时,我在前面加上以下几行:

declare @feedID int
select @feedID=20

declare @feedDate datetime
select @feedDate='1/2/2009'

从我的 C# 程序调用它时,参数是通过SqlCommand.Parameters属性传入的。

出于本讨论的目的,您可以假设参数永远不会改变,因此我们可以排除次优参数异味的原因。

4

6 回答 6

185

有时使用OPTION(RECOMPILE)是有意义的。根据我的经验,唯一可行的选择是在您使用动态 SQL 时。在您探索这对您的情况是否有意义之前,我建议您重建您的统计数据。这可以通过运行以下命令来完成:

EXEC sp_updatestats

然后重新创建你的执行计划。这将确保在创建执行计划时将使用最新信息。

每次执行查询时,添加OPTION(RECOMPILE)都会重建执行计划。我从未听说过这样的描述,creates a new lookup strategy但也许我们只是对同一件事使用不同的术语。

创建存储过程时(我怀疑您正在从 .NET 调用 ad-hoc sql,但如果您使用的是参数化查询,那么这最终会成为存储过程调用)SQL Server 尝试确定此查询的最有效执行计划根据你数据库中的数据和传入的参数(参数嗅探),然后缓存这个计划。这意味着,如果您在数据库中有 10 条记录的情况下创建查询,然后在有 100,000,000 条记录时执行它,则缓存的执行计划可能不再是最有效的。

总而言之-我认为OPTION(RECOMPILE)这里没有任何好处。我怀疑你只需要更新你的统计数据和你的执行计划。根据您的情况,重建统计信息可能是 DBA 工作的重要组成部分。如果您在更新统计信息后仍然遇到问题,我建议您同时发布两个执行计划。

并回答您的问题 - 是的,我会说每次执行查询时重新编译执行计划的最佳选择是非常不寻常的。

于 2014-01-01T03:41:03.153 回答
166

通常,当查询的运行与运行之间存在巨大差异时,我发现它通常是 5 个问题之一。

  1. 统计数据- 统计数据已过时。数据库存储有关表和索引的各个列中值类型的范围和分布的统计信息。这有助于查询引擎为如何进行查询制定攻击“计划”,例如,它将使用哪种方法来匹配表之间的键,使用哈希或查看整个集合。您可以对整个数据库或某些表或索引调用更新统计信息。这会减慢查询从一次运行到另一次运行的速度,因为当统计信息过时时,查询计划很可能对于同一查询的新插入或更改的数据不是最佳的(稍后将详细解释)。在生产数据库上立即更新统计信息可能不合适,因为根据要采样的数据量,会有一些开销、减慢和滞后。您还可以选择使用全面扫描或抽样来更新统计信息。如果您查看查询计划,您还可以查看正在使用的索引的统计信息,例如使用命令DBCC SHOW_STATISTICS(表名,索引名)。这将向您显示查询计划用来作为其方法基础的键的分布和范围。

  2. 参数嗅探- 缓存的查询计划对于您传入的特定参数不是最佳的,即使查询本身没有更改。例如,如果您传入的参数仅检索 1,000,000 行中的 10 行,则创建的查询计划可能使用 Hash Join,但是如果您传入的参数将使用 1,000,000 行中的 750,000 行,则创建的计划可能是索引扫描或表扫描。在这种情况下,您可以告诉 SQL 语句使用选项OPTION (RECOMPILE)或让 SP 使用 WITH RECOMPILE。告诉引擎这是一个“一次性使用计划”,而不是使用可能不适用的缓存计划。没有关于如何做出这个决定的规则,它取决于知道用户使用查询的方式。

  3. INDEXES - 查询可能没有更改,但其他地方的更改(例如删除非常有用的索引)会减慢查询速度。

  4. ROWS CHANGED - 您查询的行在每次调用中都会发生巨大变化。在这些情况下,通常统计信息会自动更新。但是,如果您正在构建动态 SQL 或在紧密循环中调用 SQL,则有可能您正在使用基于错误的大量行数或统计信息的过时查询计划。再次在这种情况下OPTION (RECOMPILE)很有用。

  5. 逻辑它的逻辑,您的查询不再有效,它对少量行很好,但不再扩展。这通常涉及对查询计划的更深入分析。例如,您不能再批量执行操作,而是必须分块并执行较小的提交,或者您的交叉产品适用于较小的集合,但现在随着规模扩大而占用 CPU 和内存,这可能也适用于使用 DISTINCT,您正在为每一行调用一个函数,您的键匹配不使用索引,因为 CASTING 类型转换或 NULLS 或函数......这里有太多的可能性。

通常,当您编写查询时,您应该对某些数据在表中的分布方式有一些大致的了解。例如,一列可以有多个均匀分布的不同值,或者它可以是倾斜的,80% 的时间有一组特定的值,无论分布会随着时间的推移频繁变化还是相当静态。这将使您更好地了解如何构建有效的查询。但是,在调试查询性能时,也有一个基础,可以建立关于为什么它缓慢或低效的假设。

于 2014-05-29T20:34:07.993 回答
35

要添加 OPTION(RECOMPILE) 可能非常有用的情况的优秀列表(由@CodeCowboyOrg 提供),

  1. 表变量。当您使用表变量时,表变量不会有任何预先构建的统计信息,这通常会导致查询计划中的估计行与实际行之间存在较大差异。对带有表变量的查询使用 OPTION(RECOMPILE) 可以生成一个查询计划,该计划对所涉及的行数有更好的估计。在添加 OPTION(RECOMPILE) 之前,我对一个不可用的表变量进行了特别关键的使用,我打算放弃它。运行时间从几小时缩短到几分钟。这可能是不寻常的,但无论如何,如果您正在使用表变量并致力于优化,那么值得看看 OPTION(RECOMPILE) 是否有所作为。
于 2015-10-04T01:52:52.753 回答
3

调整查询之前的第一个操作是对索引和统计信息进行碎片整理/重建,否则您就是在浪费时间。

您必须检查执行计划以查看它是否稳定(更改参数时相同),如果不是,您可能必须创建一个覆盖索引(在这种情况下为每个表)(知道系统您可以创建一个对其他查询也很有用)。

例如:创建索引 idx01_datafeed_trans On datafeed_trans (feedid, feedDate) INCLUDE(acctNo, tradeDate)

如果计划是稳定的或者您可以稳定它,您可以使用 sp_executesql('sql sentence') 执行语句以保存并使用固定的执行计划。

如果计划不稳定,您必须每次使用临时语句或 EXEC('sql sentence') 来评估和创建执行计划。(或“重新编译”的存储过程)。

希望能帮助到你。

于 2015-10-23T12:49:54.487 回答
3

解决这个问题,但似乎没有人考虑过一种解释。

统计数据 - 统计数据不可用或具有误导性

如果以下所有条件都为真:

  1. feedid 和 feedDate 列可能高度相关(例如,feed id 比 feed 日期更具体,而 date 参数是冗余信息)。
  2. 没有将两列都作为顺序列的索引。
  3. 没有涵盖这两列的手动创建的统计信息。

然后 sql server 可能错误地假设列是不相关的,导致应用这两个限制的基数估计低于预期的基数估计,并且选择了一个糟糕的执行计划。在这种情况下,解决方法是创建一个连接两列的统计数据对象,这不是一项昂贵的操作。

于 2019-03-07T08:59:00.650 回答
0

OPTION (RECOMPILE)用于真实单词生成场景。我用它来消除参数的味道并优化大查询。这可能是您问题的答案,但迹象表明优化未知数(如局部变量)也可以解决问题。

我当然不会因为几年前修复了一个时间错误而避免选择该选项。的主要风险OPTION (RECOMPILE)是使用不当,例如高频请求。

于 2021-08-23T09:46:52.957 回答