存储过程的突然执行计划


15

我试图了解SQL Server 2000所遇到的问题。我们是一个中等交易量的网站,我们有一个存储的proc调用sp_GetCurrentTransactions,它接受一个customerID和两个日期。

现在,根据日期和客户,此查询可以返回从零到1000s的行。

问题是:我们经历过的是,突然之间Execution Timeout Expired,当特定客户端尝试执行该存储的proc时,我们将获得许多错误(通常是类似错误)。因此,我们检查了查询,在SSMS中运行该查询,发现它花费了30秒。因此,我们重新编译存储的proc,然后-bang-它现在可以在300ms内运行。

我已经与我们的DBA谈过此事。他告诉我,当我们创建存储的proc时,数据库创建了一个查询计划。他说这对那组参数来说是一个很好的计划,但是如果您向它扔一些参数,那么该计划将不是该数据的最佳计划,因此您会发现它运行缓慢。

呈现给我的选项是将问题查询从存储的proc转移回动态SQL,该SQL具有每次运行时创建的执行计划。

这感觉就像是退后了一步,我觉得必须有一种解决方法。还有其他方法可以解决此问题吗?

任何和所有答复表示赞赏。


proc中是否有if / else语句?我已经看到,当计划被缓存在if语句中,然后尝试使用错误的计划在else块下执行时,就会发生这种情况。这些错误是否与proc的变化相对应?
杰里米·格雷

@杰里米:没有变化的过程,没有其他/ if语句。
Ciaran Archer

Answers:


14

此问题称为参数嗅探。

更高版本的SQL Server在处理它时为您提供了更多选择,例如OPTION (RECOMPILE)OPTIMIZE FOR提示。

您可能会尝试在存储过程中声明变量,将参数值分配给变量,并使用变量代替参数,因为听起来好像大多数时候您都在获得合理的计划。

通常,灾难性最差的计划是针对选择性很高的参数编译的,但针对选择性很低的参数运行的计划。

假设使用这种方法生成的计划更加健壮,并且对于所有参数值都令人满意,那么与JNK所建议的方法相比,该方法的优势在于,它不会为每次调用带来编译成本。

缺点是,对于某些执行,运行时间可能比针对那些参数值量身定制的计划的运行时间更长,因此这是编译时间与执行时间之间的折衷。


3
还是Oracle术语中的“绑定偷看”
Gaius

感谢@Gaius,不胜枚举一个以上RDBMS的术语;)
AndreiRînea2012年

6

除了使用动态SQL,您始终可以将proc调用更改为:

EXEC Database.dbo.usp_Myprocedure 'Parameter' WITH RECOMPILE

WITH RECOMPILE每当执行计划时,这些力量(您猜对了!)都会重新编译执行计划。

您还可以WITH RECOMPILE在存储过程的定义中包括:

CREATE PROCEDURE usp.MyProcedure (Parameters)
WITH RECOMPILE
AS
...

2

您也可以尝试确定要使用哪个数据库,尽管您会与优化器进行一些斗争,因此它比您希望的更脆弱。

技术就是这样-将存储过程分成2个,一个用于一组参数,另一个用于一组参数。在每个子句中添加where子句,以使它们之间涵盖所有可能的情况。查看查询计划-一个应该针对一组参数进行优化,而另一个针对另一组参数进行优化。您可能必须修改查询才能使这种情况发生,或者对于您的查询可能无法实现,在这种情况下,这种方法将行不通。

现在,使您的原始存储过程检查参数值,并将其分派到上一段中的两个存储过程中的适当一个。

这可以工作,但是可以迫使优化器为您的查询更有效地工作。像所有此类黑客一样,在数据库的未来版本中,它可能是不必要的,甚至会使情况变得更糟。因此,即使有效,您也必须决定是否值得。



0

嗯...如果我们只专注于这个存储过程,那么使用缓存的执行计划会引起您所看到的问题,我会感到惊讶。我会要求使用一组针对客户的参数和两个日期来查看存储过程的执行计划。我想知道更具体的索引是否有帮助->例如在customerId上,以及仅两个日期?


2
为什么会感到惊讶?参数嗅探是带有这些症状的一个相当普遍的问题,看起来好像DBA已将其识别为问题。
马丁·史密斯

@MartinSmith-我有点惊讶的是,知道参数嗅探的DBA却不知道重新编译的提示……
JNK

@JNK-是的。不知道为什么他们不提那件事。
马丁·史密斯

0

突然降低的性能听起来像是生成了效率低下的查询计划,这可能是由于缺少统计信息造成的。运行设置了“错误和警告”事件类别的SQL Server分析器,然后查看是否存在有关丢失统计信息的警告。

您可能还缺少索引,或者可能需要对索引进行碎片整理,因为它们可能过于分散而无法使用SQL Server,导致人们认为表扫描会产生较少的I / O。

@JNK提出了有关存储过程的重要建议-这些将被预先编译,查询计划将与存储过程一起存储。

我不一定同意使用WITH RECOMPILE,因为这样您就失去了存储和重复使用查询计划的好处。在某些情况下,这是有必要的-例如,如果两次调用之间基础表中的分布统计差异很大,但是通常,一旦表中的数据成熟,表中数据的分布变化将很小。

因此,总结一下:

  1. 检查缺少的统计信息
  2. 检查索引碎片
  3. 创建并使用存储的过程
  4. 请重命名proc-sp_是内部系统SQL Server proc的一个软保留的前缀名称空间-导致SQL Server始终首先在master数据库中查找那些存储过程。重命名proc usp_而不是sp_将导致性能提高,但是在这种情况下,我怀疑它是您的问题。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.