更改过程或触发器中的SET选项将致使从新编译

SQL Prompt根据数据库的对象名称、语法和代码片断自动进行检索,为用户提供合适的代码选择。自动脚本设置使代码简单易读--当开发者不大熟悉脚本时尤为有用。SQL Prompt安装便可使用,能大幅提升编码效率。本教程介绍了SQL Prompt的性能规则PE012,该规则将建议您是否在存储过程或触发器中检测到SET语句的使用,这可能会致使没必要要的从新编译,尽管问题涉及其余类型的批处理。sql

有时,因为某种显而易见的缘由,您将有一个存储过程或触发器间歇地花费更长的时间运行。您已经检查了索引,排除了诸如参数嗅探之类的问题,可是间歇性的性能问题仍然存在。SET为了更改执行设置,是否能够像您在批处理中发出语句那样简单呢?若是这样作,则多是因为SQL Server须要从新编译该过程或重复触发而致使了该问题。数据库

从新编译没有什么特别的错误,实际上,强制执行某些查询在每次执行时从新编译是很常见的,正是为了不与参数嗅探、滥用Execute()或一应俱全的查询有关的不良性能问题。可是,若是从新编译变得过多,尤为是对于频繁或昂贵的查询,则可能会成为问题,值得调查缘由,我将向您展现如何使用扩展事件。缓存

什么是从新编译?安全

当SQL Server执行临时批处理或查询或诸如存储过程或触发器之类的对象时,SQL Server将为每一个批处理或对象以及该批处理或对象中的每一个查询编译针对当前状态进行优化的执行计划数据库,其对象及其数据。SQL Server的优化器设计此计划须要花费时间和资源,可是必须在代码能够传递到执行引擎以前完成。幸运的是,咱们倾向于重复执行相同的查询或过程,可能使用不一样的参数,所以SQL Server将其生成的大多数计划存储在计划缓存中,而且不管咱们使用什么参数值,都将确保全部计划均可以安全地重用。当咱们再次执行相同的批处理或对象时,只要有可能,它将简单地重用其缓存的计划。数据结构

可是,有时咱们会从新执行存储过程,或者从新提交批处理或查询优化器以前已见过的缓存,而且针对该优化器在缓存中具备优化的计划,可是因为某些缘由,它没法重用该计划并编译一个新的。这是从新编译,而且因为各类缘由而发生。若是执行引擎检测到表已更改或其统计信息已发生重大变化,它将自动发生,这时它将标记要从新编译访问该表的查询的全部缓存计划。下次运行其中一个查询时,优化器将生成新计划,而旧计划将被删除。ide

咱们还能够经过将OPTION (RECOMPILE)提示附加到查询来强制优化器不断从新编译计划。该查询的计划可能仍在高速缓存中,但不会被重用。一般这样作是为了处理因为参数嗅探,使用“catch-all”过程,滥用Execute()等等所致使的不稳定性能。sqlserver

为了节省时间和资源,SQL Server会在可能的状况下进行语句级的从新编译。若是批处理或存储过程当中仅一个语句的计划因数据结构或数据的基础更改而无效,或者只有一个语句具备OPTION (RECOMPILE)提示,则仅从新编译受影响的语句的计划,而不从新编译整个批处理或存储。性能

有时,从新编译既不会因数据结构或数据的更改而自动触发,也不会因为使用提示而被强制执行。咱们在同一数据库上从新执行相同的查询,存在一个匹配的缓存计划,由于提交的查询的SQL文本和与该缓存计划相关联的SQL文本彻底匹配(包括空格和回车符),可是该计划没有被重用。测试

再次,有几种可能的缘由,咱们将不在这里进一步讨论,例如,对未在过程当中静态建立的临时表的引用,或者缺乏模式验证,而咱们将要解决的缘由是缓存的计划是使用与提交查询的链接所使用的SET选项不一样的SET选项建立的。优化

“影响计划重用”的SET选项

更改某些SET选项的值(有时称为“影响计划重用”的选项)将更改查询的运行方式及其结果。所以,当优化器检查其缓存计划是否匹配时,它包括检查在编译缓存计划中使用的SET选项是否与发布批次的链接中使用的SET选项匹配。若是它们不匹配,则它将不会重复使用现有计划,而是会编译一个新计划。

这意味着您能够看到多个缓存的计划,除了这些SET选项的细节外,它们基本上是相同的。

这些“计划重用影响”选项,按字母顺序排列,ANSI_DEFAULTS、ANSI_NULL_DFLT_OFF、ANSI_NULL_DFLT_ON、ANSI_NULLS、ANSI_PADDING、ANSI_WARNINGS、ARITHABORT、CONCAT_NULL_YIELDS_NULL、DATEFIRST、DATEFORMAT、FORCEPLAN、LANGUAGE、NO_BROWSETABLE、NUMERIC_ROUNDABORT和QUOTED_IDENTIFIER。

当SQL Server在编译过程当中执行“恒定折叠”时,会检测到这些SET语句,而且彷佛在旧版本的SQL Server中,每次调用该过程时,将其中某些SET选项更改成某些值可能会致使从新编译。可是,在最新版本的SQL Server中,不多听到此问题。

可是,明智的改变是SET选项,在批处理开始时,甚至在触发器过程内更改选项,能够致使编译新计划,只有在执行彻底相同的批处理或对象,具备彻底相同的设置时,才能够从新使用该计划。虽然以这种方式从新编译计划不多会引发主要的性能问题,但确实会带来CPU成本,而且可能会引发问题,尤为是对于编译成本高且执行频率高的复杂查询,甚至可能同时出现这两种状况在多语句程序中。

更改链接设置

对于ODBC、ADO或JDBC链接,为链接的默认设置指定任何更改的方法是,在首次创建链接后执行初步的SET语句批处理。链接字符串中没有容许该操做的选项:必须由SET语句完成。在SSMS中,您可使用“查询”菜单(“查询” >“查询选项”)为链接的执行行为指定高级和ANSI标准选项。在进行开发和测试时,值得将它们设置为与生产系统链接所使用的相同。这些设置仅反映创建链接时的执行设置。若是随后在链接中的批次中更改设置,则这些设置将用于后续批次。

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(上)

您会注意到,此选项卡(和ANSI选项卡,没有显示)中的SET选项没有涵盖全部“计划-重用-影响”选项。其他的操做必须在经过SET选项语句创建新链接时完成。

经过更改SET选项更改结果

如前所述,会话SET选项的更改在某些状况下可能致使错误或警告,或者致使查询的结果不一样。快速演示值得一提,在这里,我将在每批开始时简单地更改几个SET选项的值:


在ARITHABORT设置为ON的状况下,查询遇到0除时,查询将以错误(咱们捕获到这个错误)结束,所以返回2行。当咱们关闭此选项时,同一查询将返回3行:

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(上)

若是检查每一个批次的计划,除了这些SET选项的值(打开SELECT操做符的属性以查看它们)以外,您将看到它们是相同的:

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(上)

如下查询将向咱们展现计划缓存中的状况(我已经在PhilFactor数据库中完成了此操做,所以您须要进行更改)。


获得这个结果…

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(上)

因为SET选项设置不一样(235和4331),每一个批次都有本身的编译计划。您会注意到,该计划的一个属性set_options,为您提供了全部SET选项的位图值,其中大多数选项为on或off。

每次更改这些设置选项中的一个时,您都会看到专门为该选项集建立的新计划,这显然会增长对缓存的要求以及编译计划所花费的CPU时间。若是您对这两个批次执行十次,您将看到使用了适当的计划,而无需从新编译。

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(上)

在存储过程当中更改SET选项

到目前为止,咱们仅处理批处理,可是若是因为某种缘由要确保使用特定设置执行各个过程该怎么办?

我已经将相同的逻辑封装在三个存储过程当中,前两个对咱们的两个选项使用了特定的设置,而第三个没有任何SET选项语句。

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(下)

我对这三个过程分别执行了两次,首先是在全部选项均使用“默认”设置的会话中进行,其中ARITHABORT和ANSI_WARNINGS均处于ON状态(set_options = 4347),而后从前者处于ON状态然后者处于OFF状态的会话中(4331),最后从两个都关闭的会话中(235)。

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(下)

咱们总共看到9个计划,每次从具备不一样set_options值的链接执行该计划时,都会为每一个过程编译一个新计划。换句话说,若是调用批处理的执行设置与编译该过程的任何执行计划时有效的执行设置不匹配,则会使用新的set选项建立一个新的缓存计划。若是咱们使用链接相同set_options值从新执行相同的存储过程,则该计划将被重用。

调用第一个存储过程(显式设置ARITHABORT为ON)始终返回2行,而调用第二个存储过程始终返回3行。在不使用SET语句的状况下调用过程时,它仅取决于调用链接的设置。

若是您更改了过程当中的设置,则它们仅在该过程当中有效,所以它们不会影响调用该过程的批处理。全部9个计划都显示了用于执行调用批处理的链接的SET选项值。

在过程和触发器中捕捉“影响计划的重用”的SET语句的使用

SQL Prompt中的性能规则(PE012)看起来是否SET在存储过程和触发器(尽管不是批处理)中作出了任何“影响计划重用”的SET语句。您还可使用SQL Change Automation运行检查,以在数据库构建源中发现此问题。SQL Monitor还支持代码分析。

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(下)

不过请注意:这种现象不只适用于过程或触发器,并且还适用于任何临时批处理、使用sp_executesql执行的批、准备好的查询和动态SQL。若是发出“影响计划重用”SET语句,则对于其中任何一个的缓存计划都没法如此轻松地重用,而且在SQL Server的早期版本中,每次使用都会有从新编译的风险。

咱们优先使用存储过程和触发器来处理动态Transact-SQL批处理,由于它们更易于重用。它们是参数化的,所以SQL文本永不更改,从而促进了重用。在准备好的批次或过程当中更改设置时,设置选项仅用于执行准备好的批次或过程,

批处理也能够重用,可是若是经过sp_executesql或Prepare方法(而不是动态SQL或Execute方法)执行批处理,SQL Server发现这样作更容易。

更糟糕的是,在执行临时批处理时,SET选项中的任何更改都会从该批处理中泄漏出来,从而使链接保留其新设置:您必须显式还原设置,可是在该点以前当即停止该批处理的错误,将没法执行代码。而后,优化器可能须要编译新计划,以针对您在该链接上执行的全部后续批处理和过程的这些新设置。

很难检测到此错误,它加强了如下通常建议:在创建链接后,这些语句必须始终做为初步批处理执行,而且随后避免任何更改。这意味着全部此类SET语句在代码中都是可疑的,应被视为“SQL代码气味”。很难证实它们的合理性。

调查过分从新编译

在扩展事件不可用或过于粗糙的SQL Server版本中,可使用SQL Server Profiler。尽管SP:Recompile跟踪事件能够仅用于报告过程和触发器的语句级从新编译,但SQL:StmtRecompile也能够用于跟踪和调试从新编译,它能够检测存储过程,触发器,临时批处理的从新编译,使用sp_executesql,准备好的查询和动态SQL执行的批处理。SP:Recompile和SQL:StmtRecompile的event子类列包含一个整数代码,指出从新编译的缘由。

经过扩展事件,事情变得更加文明。咱们能够得到有关从新编译及其缘由的完整报告。这是一个简单的会话,用于报告各个编译。

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(下)

这样,咱们能够得到单个从新编译的详细信息。我一般在sqlserver.username字段上添加会话事件过滤器,以仅针对特定用户(运行测试代码的测试用户的名称)得到从新编译。不然会产生不少噪音。

SQL Prompt使用教程:更改过程或触发器中的SET选项将致使从新编译(下)

总结

若是您发现代码中包含涉及“计划重用影响”选项的SET语句,那么这就是代码的味道,您应该调查缘由。

您固然能够作一些狡猾而聪明的事情,可是在我从事SQL Server开发的工做中,我从未发现过。这不只是存储过程或触发器中的不良作法,并且还可能以任何批次执行屡次。若是须要设置语言、ANSI选项或错误处理兼容性,则在建立链接并建立单个标准时进行设置。若是这样作失败,则会致使SQL Server执行没必要要的从新编译。

当我写这些SET语句的使用是“很差的”时,我并不但愿暗示批处理的从新编译必定是很差的:有时它们避免了一些隐匿的性能问题之一,而且它们不多会影响性能只要不沉迷于SQL代码,应用程序的气味就没必要要了。例如,当咱们建立要重用的批处理时,咱们老是经过与参数sp_ExecuteSQL一块儿使用来促进代码重用,或者在应用程序中,咱们正确地使用绑定参数。为了谨慎起见,咱们使用表变量。

相关文章
相关标签/搜索