【问题标题】:Why does the SqlServer optimizer get so confused with parameters?为什么 SqlServer 优化器会与参数混淆?
【发布时间】:2010-09-29 16:25:21
【问题描述】:

我知道这与参数嗅探有关,但我只是对以下示例之类的事情感到困惑,即使是一项可以很好地完成许多复杂事情的技术。

我们中的许多人都遇到过存储过程,它间歇性地运行比平时慢几个数量级,然后如果你从过程中复制出 sql 并在单独的查询窗口中使用相同的参数值,它的运行速度也一样快像往常一样。

我只是通过转换这个来修复这样的过程:

alter procedure p_MyProc
(
    @param1 int
) as -- do a complex query with @param1

到这里:

alter procedure p_MyProc
(
    @param1 int
)
as

declare @param1Copy int;
set @param1Copy = @param1;

-- Do the query using @param1Copy

它从一分钟多的运行时间缩短到一秒钟以内,就像它通常运行的那样。这种行为似乎完全随机。对于 10 个 @param1 输入中的 9 个,查询速度很快,无论最终需要处理多少数据,或者结果集有多大。但是对于十分之一的人来说,它只是迷路了。解决方法是在查询中用相同的 int 替换一个 int?

没有意义。

[编辑]

@gbn 链接到这个问题,其中详述了一个类似的问题:

Known issue?: SQL Server 2005 stored procedure fails to complete with a parameter

我不敢喊“虫子!”因为这通常是一种逃避,但这对我来说确实是一个错误。当我使用相同的输入运行存储过程的两个版本时,我看到了相同的查询计划。唯一的区别是原版运行需要一分钟多,而带有傻瓜参数复制的版本会立即运行。

【问题讨论】:

  • 是否总是相同的值导致它变慢?数据库中是否同时发生了其他事情?您正在运行什么版本的 sql server(最新的服务包?)
  • Sql Server 2005, 9.00.3073.00。在这种情况下,参数是列表表的 PK,查询分析不同表中列表中的项目。无论数据库上的活动如何,我都可以使用大约十分之一的列表 ID 来重现它。运行两个版本时的查询计划是相同的。
  • 在这些情况下执行是否不同?我知道计划是一样的,但是 sql profiler 说 proc 中每个语句的持续时间、读写计数是什么?
  • 正确 - 设置统计 IO 和设置统计时间。如果统计数据相同,您就知道它被重创了。
  • 它没有调用任何子 SP 是吗?这些肯定是有问题的。

标签: sql sql-server optimization


【解决方案1】:

我知道这是一个有 2 年历史的帖子,但它可能会对以后的人有所帮助。

一旦您分析了查询执行计划并知道这两个计划之间的区别(查询本身和在存储过程中执行有缺陷计划的查询),您可以使用查询提示修改存储过程中的查询解决问题。这适用于查询在存储过程中执行时使用不正确索引的情况。您可以在程序的适当位置的表格之后添加以下内容:

SELECT col1, col2, col3
FROM YourTableHere WITH (INDEX (PK_YourIndexHere))

这将强制查询计划使用应该解决问题的正确索引。这并不能解释为什么会发生这种情况,但它确实提供了一种解决问题的方法,而无需担心复制参数以避免参数嗅探。

【讨论】:

    【解决方案2】:

    十分之一的人给出了错误的缓存计划。

    RECOMPILE 增加了开销,屏蔽允许根据其自身的优点评估每个参数(非常简单)。

    如果计划错误,如果 10 个中的 1 个在索引 1 上生成扫描,而其他 9 个在索引 2 上生成查找,该怎么办?例如,10 中的 1 是,比如说,50% 的行?

    编辑:其他问题

    编辑 2:

    重新编译不起作用,因为参数在编译时被嗅探。
    从其他链接(粘贴):

    这个article 解释...

    ...parameter values are sniffed during compilation or recompilation...
    

    最后(编辑 3):

    参数嗅探在当时可能是一个好主意,并且可能大部分时间都很好用。我们将它全面用于将在 WHERE 子句中结束的任何参数。 我们不需要使用它,因为我们知道只有少数(更复杂的,例如报告或许多参数)可能会导致问题,但我们使用它是为了保持一致性。

    当用户抱怨时它会回来咬我们,我们应该使用掩蔽......

    【讨论】:

    • 很难说,因为当这种情况发生时,我注意到查询最终完成时执行时间是错误的。图形计划看起来相同,但在这种情况下我不相信它们。
    • 我已确认计划显示为相同,都是寻求。
    • 感谢@gbn 的链接。第一个让我认为这实际上是一个错误,而不是一些模糊的预期参数嗅探行为。
    【解决方案3】:

    在将我的代码从测试服务器移动到生产服务器时,我反复遇到这个问题 - on two different builds of SQL Server 2005。我认为在 SQL Server 2005 的某些版本中,参数嗅探存在一些大问题。我在开发服务器或两个本地开发人员版本盒上从未遇到过这个问题。我从未见过它在 SQL Server 2000 或任何可以追溯到 6.5 的版本上出现如此大的问题。

    在我发现它的情况下,唯一的解决方法是使用参数屏蔽,我仍然希望 DBA 将生产服务器修补到 SP3,这样它可能会消失。没用的东西:

    • 在 EXEC 或 SP 本身中使用 WITH RECOMPILE 提示。
    • 删除并重新创建 SP
    • 使用 sp_recompile

    请注意,在我正在处理的情况下,自之前的调用以来数据并没有改变 - 我只是将代码编写到已经加载数据的生产框上。自 SP 存在以来,所有调用都没有对数据进行任何更改。

    哦,如果 SQL Server 不能在没有掩码的情况下处理这个问题,他们需要添加一个参数修饰符 NOSNIFF 或其他东西。如果你屏蔽了所有参数会发生什么,所以你有@Something_parm 和@Something_var 并且有人更改了代码以使用错误的代码,突然你又遇到了嗅探问题?另外,您正在污染 SP 中的命名空间。我正在“修复”的所有这些 SP 让我抓狂,因为我知道对于经验不足的工作人员来说,它们将是一场维护噩梦,我将把这个项目交给一天。

    【讨论】:

    • 是的,我读到有些人总是屏蔽每个过程中的每个参数。哎呀,真是痛苦。这绝对是一个错误。我在 9.00.3073.00,我很确定这是最新的更新。
    【解决方案4】:

    这是计划缓存的问题,它并不总是与参数相关,就像在您的场景中那样。

    (当proc在第一次运行时使用异常参数调用时会出现参数嗅探问题,因此缓存计划对那些奇数值非常有效,但在大多数其他调用proc时很糟糕。)

    当应用团队从生产服务器上高度使用的日志表中删除所有旧记录时,我们遇到了类似的情况。删除记录可以提高性能,对吗?不,性能立即下降。

    原来,一个常用的存储过程在表快为空的时候被重新编译,它缓存了一个非常糟糕的执行计划(“嘿,这里只有 50 条记录,还不如做一个表扫描!”) .无论初始参数如何,都会发生。

    我们的解决方法是使用 sp_recompile 强制重新编译。

    【讨论】:

      【解决方案5】:

      是否对参数的每个查询引用都将其与 int 值进行比较,没有函数且没有强制转换?

      您能否使用该参数增加任何表达式的特异性,以使使用多字段索引的可能性更大?

      【讨论】:

      • 没有函数,没有强制转换。我的两个版本的 proc 使用相同的查询计划运行,但原始版本会挂起一分钟。
      • 这种事情我见多了。不过,几乎每次都有“Doh!”当我终于弄清楚我做错了什么的那一刻。
      【解决方案6】:

      您能否在 SQL Profiler 上检查快速和慢速时的读取次数和执行时间?它可能与获取的行数有关,具体取决于参数值。这听起来不像是缓存计划问题。

      【讨论】:

        【解决方案7】:

        是否有可能提供的参数值有时不是int?

        【讨论】:

        • 没有。我可以用特定的整数重现它。
        【解决方案8】:

        如前所述,这是一个编译问题。如果您恢复程序,此问题是否仍然存在?如果再次发生这种情况以强制重新编译,您可以尝试的一件事是使用:

        sp_recompile [ @objname = ] 'object'

        BOL 关于@objname 参数的权利:

        是当前数据库中存储过程、触发器、表或视图的限定或非限定名称。对象是 nvarchar(776),没有默认值。如果 object 是存储过程或触发器的名称,则存储过程或触发器将在下次运行时重新编译。如果 object 是表或视图的名称,则所有引用该表或视图的存储过程将在下次运行时重新编译。

        如果您删除并重新创建该过程,您可能会导致客户端在尝试执行该过程时失败。您还需要重新应用安全设置。

        【讨论】:

        • 不,重新编译没有效果。
        【解决方案9】:

        这可能是由于 SQL Server 为它们编译了存储过程和 缓存 执行计划,而缓存的执行计划可能不适合这组新参数。你可以试试WITH RECOMPILE选项看看是不是这个原因。

        EXECUTE MyProcedure [parameters] WITH RECOMPILE
        

        WITH RECOMPILE 选项将强制 SQL Server 忽略缓存的计划。

        【讨论】:

        • ALTER PROCEDURE 导致 proc 重新编译。您可以只运行它或 DROP/CREATE 并解决问题。
        • WITH RECOMPILE 表示以后每次运行时都会重新编译,因此您不必等待它变慢然后运行 ​​ALTER 或 DROP 和 CREATE。
        • 不,重新编译没有效果。这是我尝试的第一件事。
        • 我有两个 procs,除了参数复制之外完全相同。我用相同的输入运行它们并看到相同的计划,只是执行时间有很大差异。我来回运行它们多次以排除缓存问题。
        猜你喜欢
        • 1970-01-01
        • 2017-07-15
        • 2021-11-22
        • 2016-01-22
        • 1970-01-01
        • 2021-01-07
        • 1970-01-01
        • 2020-10-20
        相关资源
        最近更新 更多