23

我在 SQL Server 2012 中遇到了经典的参数嗅探问题。根据一些研究,我发现了围绕这个问题的多种选择。我需要了解两者之间区别的两个选项是OPTION(OPTIMIZE FOR UNKNOWN)vs OPTION(RECOMPILE)

我犹豫是否OPTION(RECOMPILE)在遇到此问题的查询结束时使用它,因为它会强制服务器每次生成一个新的执行计划。如果我经常调用这个查询,这会增加那台机器的 CPU。

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

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

4

3 回答 3

30

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

是的,它会的。


MSDNOPTION(OPTIMIZE FOR UNKNOWN)的引用中可以看出和之间有两个主要区别:OPTION(RECOMPILE)

OPTIMIZE FOR UNKNOWN

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

RECOMPILE

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

因此,两个主要区别是:

  1. 缓存(或不缓存)查询计划。

通常生成的查询计划会被缓存并重用。OPTIMIZE FOR UNKNOWN不影响引擎的此功能。RECOMPILE抑制此功能并告诉引擎放弃计划而不是将其放入缓存中。

  1. 在计划生成期间使用(或不使用)实际参数值。

通常优化器“嗅探”参数值并在生成计划时使用这些值。OPTIMIZE FOR UNKNOWN抑制此功能并告诉引擎将所有参数视为其值未知。优化器具有内置规则和启发式方法,如何将可用统计信息用于各种过滤条件。请参阅优化……平庸?更多细节。通常,参数嗅探在第一次运行查询/存储过程时使用,并在第一次运行期间使用参数值。生成的计划被缓存,以后可以重用。

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

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

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

这种效果在动态搜索条件查询中最为明显。例如:

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)

If @ParamSomeIDis0优化器会将查询视为根本没有任何WHERE子句。该计划根本不会提及OtherTable

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

如果@ParamSomeID是 5,则计划将在唯一索引中进行索引查找,OtherTable并仅读取OtherTable.

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

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

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

于 2016-11-05T11:33:11.970 回答
13

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

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

OPTION(RECOMPILE)将在每次执行时强制重新编译,并且是一种相当笨拙的方法。这仅在分析 DW/BI 环境中才有意义,其中每个查询可能不同、复杂并且可能需要很长的运行时间。

您还可以使用其他选项:

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

于 2016-11-05T07:55:27.287 回答
5

我都用过。OPTION(OPTIMIZE FOR UNKNOWN)用于接受各种参数的繁重搜索存储过程。有一些我不知道的条件(统计数据等等)会导致优化失败,查询很普通,但是会导致严重的延迟(甚至超时)。OPTION(OPTIMIZE FOR UNKNOWN)解决了这个问题,但并不理想。

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

存储过程的核心推动了“键入结果”解决方案,其中每三个击键将触发数据库搜索,结果将填充到下拉列表中。

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

于 2016-11-05T04:15:38.023 回答