OPTION(OPTIMIZE FOR UNKNOWN)和OPTION(RECOMPILE)之间的主要区别是什么?

时间:2016-11-04 22:54:16

标签: sql-server tsql sql-server-2012 parameter-sniffing

我遇到了SQL Server 2012中的经典Parameter Sniffing问题。基于一些研究,我发现了围绕这个问题的多种选择。我需要了解两者之间的区别是OPTION(OPTIMIZE FOR UNKNOWN)OPTION(RECOMPILE)

我在我遇到此问题的查询结束时使用OPTION(RECOMPILE)时犹豫不决,因为它会强制服务器每次都生成一个新的执行计划。如果我经常调用此查询,这将会占用该计算机的CPU。

因此,我使用他最好的解决方案,这两个选项之间的真正区别是什么?

OPTION(OPTIMIZE FOR UNKNOWN)会重用缓存而不是每次重新编译吗?

3 个答案:

答案 0 :(得分:10)

  

OPTION(OPTIMIZE FOR UNKNOWN)将重用缓存而不是重新编译   每一次?

是的,它会。

OPTION(OPTIMIZE FOR UNKNOWN)OPTION(RECOMPILE)之间存在两个主要差异,可以从MSDN的引用中看出:

  

OPTIMIZE FOR UNKNOWN

     

指示查询优化器使用统计信息   数据而不是所有局部变量的初始值时   查询已编译和优化,包括使用创建的参数   强制参数化。

  

RECOMPILE

     

指示SQL Server数据库引擎放弃该计划   执行后为查询生成,强制查询优化器   在下次执行相同查询时重新编译查询计划。   如果不指定RECOMPILE,数据库引擎会缓存查询计划   并重复使用它们。编译查询计划时,RECOMPILE查询提示   使用查询中任何局部变量的当前值,如果   查询在存储过程内,传递给当前值   任何参数。

因此,两个主要区别是:

  1. 缓存(或不缓存)查询计划。
  2. 通常会生成并重用生成的查询计划。 OPTIMIZE FOR UNKNOWN不会影响引擎的此功能。 RECOMPILE禁止此功能并告诉引擎放弃计划而不将其放入缓存中。

    1. 在计划生成期间使用(或不使用)实际参数值。
    2. 通常是优化者"嗅探"参数值并在生成计划时使用这些值。 OPTIMIZE FOR UNKNOWN抑制此功能并告诉引擎将所有参数视为其值未知。优化程序具有内置规则和启发式,如何使用可用的统计信息来处理各种过滤条件。有关详细信息,请参阅Optimize for… Mediocre?。通常,参数嗅探在第一次运行查询/存储过程时使用,并在第一次运行期间使用参数值。生成的计划被缓存,以后可以重复使用。

      这里要记住的一个非显而易见的事情是,在这两种情况下(正常时没有任何查询提示和OPTIMIZE FOR UNKNOWN提示)生成的计划必须有效并为任何可能的参数值。它适用于在正常/无提示情况下第一次运行期间使用的嗅探值;它不是针对OPTIMIZE FOR UNKNOWN情况中的任何特定值定制的,但如果参数稍后以任何方式更改,它仍然有效。

      这很重要,它会阻止优化程序执行某些转换和简化计划。

      OPTION(RECOMPILE)允许优化器在每次运行期间内联参数的实际值,优化器使用参数的实际值来生成更好的计划。它不必担心生成的计划可能无法与其他参数值一起使用,因为该计划不会被缓存和重用。

      此效果对于Dynamic Search Conditions查询几乎可见。例如:

      SELECT ...
      FROM T
      WHERE
          (@ParamSomeID = 0)
          OR
          (
              @ParamSomeID = -1
              AND
              T.SomeID NOT IN
              (
                  SELECT OtherTable.SomeID
                  FROM OtherTable
              )
          )
          OR
          (
              T.SomeID IN
              (
                  SELECT OtherTable.SomeID
                  FROM OtherTable
                  WHERE OtherTable.SomeID = @ParamSomeID
              )
          )
      OPTION(RECOMPILE)
      

      如果@ParamSomeID0,优化程序会将查询视为根本没有任何WHERE子句。该计划根本不会提到OtherTable

      如果@ParamSomeID-1,则该计划会使用Left Anti Semi Join将T加入OtherTable并扫描整个OtherTable

      如果@ParamSomeID是5,则该计划会在OtherTable上的唯一索引中执行索引搜索,并且只读取OtherTable中的一行。

      如果没有OPTION(RECOMPILE),就不会发生这种简化和转换。

      使用OPTION(RECOMPILE)的另一个原因是您的数据分布非常偏差。例如,您有一个包含1M行的表。一列在990K行中具有值0,在1K行中具有从1到10的值。过滤此列的查询应具有不同的计划,具体取决于过滤器的实际值。

      在上述两个示例中,OPTIMIZE FOR UNKNOWN都会产生一个平庸的计划。

答案 1 :(得分:8)

  

OPTION(OPTIMIZE FOR UNKNOWN)是否会重用缓存而不是每次重新编译?

是。优化未知将影响计划的生成方式(即明确阻止它嗅探参数并将其与列数据直方图进行比较),但一旦生成,计划将保留在缓存中并重复使用。

OPTION(RECOMPILE)将强制重新编译每次执行,并且是一种相当沉重的方法。只有在每个查询可能不同,复杂且可能具有大量运行时的分析DW / BI环境中才有意义。

您还可以使用其他选项:

这两种方法都可以让您获得与帖子相同的效果,但是以非侵入性的方式(没有应用代码/查询更改)。

答案 2 :(得分:2)

我用过两者。 OPTION(OPTIMIZE FOR UNKNOWN)用于获取各种参数的重型搜索存储过程。有一些条件,我不知道(统计数据和什么不是),会抛出优化,查询是平凡的,但是,它会导致严重的延迟(甚至超时)。 OPTION(OPTIMIZE FOR UNKNOWN)解决了这个问题,但并不理想。

同样繁重的搜索程序会产生间歇性问题,这意味着几个月后搜索会超时。直接的解决方案是调用sp_recompile,这是向存储过程添加OPTION(RECOMPILE)子句的同义词。

当您键入" 解决方案时,存储过程的内容推动了"结果,其中每三次击键将触发数据库搜索,结果将填充到下拉列表中。

最后,我删除了OPTION(OPTIMIZE FOR UNKNOWN),只是在我的夜间维护工作中添加了EXEC sp_recompile<sp>,这解决了所有问题。