参数嗅探(Parameter Sniffing)(2/2)

参数嗅探(Parameter Sniffing)(1/2)里,我介绍了SQL Server里参数嗅探的基本概念和背后的问题。如你所见,当缓存的计划被SQL Server盲目重用时,会带来严重的性能问题。今天我会向你展示下如何处理这个问题,即使用不同的技术克服它。

索引(Index)

上次我们讨论造成参数嗅探问题的根源是:在执行计划里,SQL 语句有时会产生书签查找,有时会产生表/聚集索引扫描。如果你能在数据库里修改索引,解决这个问题的最简单方法就是提供查询列对应的覆盖非聚集索引。这里我们就要包含书签查找的需要列,在非聚集索引的叶子层。这样做后,就可以获得计划稳定性:不管提供的输入任何参数,查询优化器都可以编译同样的执行计划——这里就是都会用到索引查找(非聚集索引)运算符。

1 DROP  INDEX idx_Test ON Table1
2 CREATE NONCLUSTERED INDEX idx_Test ON Table1(Column2) INCLUDE(Column1)
3
4 SELECT * FROM dbo.Table1 WHERE Column2=1
5 SELECT * FROM dbo.Table1 WHERE Column2=2

如果你不能修改你的索引设计,可以尝试下面的方法:

重编译(Recompilation)

SQL Server提供给你的第一个选项是执行计划的重编译。它提供2个不同选项给你使用:

  • 全部重编译,整个存储过程
  • 有问题的SQL语句重编译,即所谓的语句级别的重编译(从SQL Server 2005起可用)

我们通过实例详细讲解下这2个选项。下面的语句会对整个存储过程进行重编译:

 1 -- Create a new stored procedure for data retrieval
 2 CREATE PROCEDURE RetrieveDataR
 3 (
 4     @Col2Value INT
 5 )
 6 WITH RECOMPILE
 7 AS
 8     SELECT * FROM Table1
 9     WHERE Column2 = @Col2Value
10 GO

当你执行这样的存储过程时,查询优化器在每次执行前都会重新编译存储过程。因此你得到的执行计划都是基于目前输入的参数值。作为重编译的副作用,你的执行计划不会被缓存,对于一个每次都重编译的执行计划进行缓存是没有意义的。当你有一个大的复杂的存储过程在存储过程级别使用RECOMPILE选项,这样做就没太大意义,因为你的整个存储每次都重编译,而存储过程就是为了编译好进行重用,从而提高执行效率。

1 EXEC dbo.RetrieveDataR @Col2Value = 1 -- int
2 EXEC dbo.RetrieveDataR @Col2Value = 2 -- int

如果你的参数嗅探问题只出现在一个特定的SQL语句。那就没有必要对整个存储过程进行重编译了。因此从SQL Server2005开始,提供称为语句级别的重编译(Statement Level Recompilation) 。你可以对需要重编译的SQL语句加上RECOMPILE查询提示而不是整个存储过程。我们来看下下面的代码:

 1 -- Create a new stored procedure for data retrieval
 2 CREATE PROCEDURE RetrieveDataR2
 3 (
 4     @Col2Value INT
 5 )
 6 AS
 7     SELECT * FROM Table1
 8     WHERE Column2 = @Col2Value
 9
10         SELECT * FROM Table1
11     WHERE Column2 = @Col2Value
12         OPTION (RECOMPILE)
13 GO

上述例子里的第2个SQL语句在存储过程执行的时候都会重编译。第1个语句在执行初始时编译好,并生成计划缓存做后续重用。在你不想修改数据库的索引时,这个方法是处理参数嗅探的推荐方法。

1 EXEC dbo.RetrieveDataR2 @Col2Value = 2 -- int

OPTIMIZE FOR

除了存储过程或SQL语句的重编译查询提示,SQL Server也提供OPTIMIZE FOR的查询提示。用这个查询提示你可以告诉查询优化器哪个参数值下,对执行计划执行优化,我们看下面的例子:

 1 -- Create a new stored procedure for data retrieval
 2 CREATE PROCEDURE RetrieveDataOF
 3 (
 4     @Col2Value INT
 5 )
 6 AS
 7     SELECT * FROM Table1
 8     WHERE Column2 = @Col2Value
 9         OPTION (OPTIMIZE FOR (@Col2Value = 1))
10 GO

从存储过程的定义中你可以看到,SQL语句的执行计划在参数@Col2Value值为1的时候需要进行优化。不管你提供给这个参数的任何值,你都获得为值1优化的编译计划。用这个方法你已经对SQL Server放大招了,因为查询优化器没别的选项——它必须为参数值1生成优化的的执行计划。当你知道查询计划需要为指定参数进行优化时,可以使用这个方法让SQL Server对此参数的执行计划进行优化。在你重启SQL Server或执行群集故障转移时,就可以预知你的执行计划。

为了进一步保障这个选项的有效性,你就要熟悉你的数据分布情况,还有什么时候数据分布情况会改变。如果数据分布情况已经改变,你就要修改查询提示,看看是否仍然合适。你不能完全相信查询优化器,因为你已经用OPTIMIZE FOR查询提示重置查询优化器的选择。要记住这个。另外在提供OPTIMIZE FOR查询提示的同时,SQL Server也提供OPTIMIZE FOR UNKNOWN查询提示。如果你决定使用OPTIMIZE FOR UNKNOWN查询提示,查询优化器就使用表统计信息里的密度来做参数预估。如果逻辑读超过了临界点,还是会使用表/索引扫描……

小结

在这个文章里我向你展示在SQL Server里处理参数嗅探问题的不同方式。其中造成这个问题的最常见原因是糟糕的索引设计,造成参数值传入后优化器在执行计划里选择了书签查找。如果这样的执行计划被缓存重用的话,你的I/O成本就会爆表。在生成环境中,我就看到因为这个原因就造成100GB的逻辑读。在SQL语句上加一个简单的RECOMPILE查询提示就可以解决这个问题,查询只会增加少量的逻辑读。

如果你不能修改数据库索引设计,你可以在存储过程或SQL语句上使用RECOMPILE查询提示。作为副作用编译的计划就不会缓存。除此外的查询提示,SQL Server还提供OPTIMIZE FOROPTIMIZE FOR UNKNOWN的查询提示。在你使用这些查询提示时,你要对你的数据和数据分布情况非常熟悉,因为你在重置优化器。请慎重使用!Be always aware of this fact!

时间: 2024-10-05 05:58:47

参数嗅探(Parameter Sniffing)(2/2)的相关文章

(4.13)参数嗅探

1 --参数嗅探 Parameter Sniffing 2013-2-8 2 3 --当使用存储过程的时候,总是要使用到一些变量.变量有两种,一种 4 --是在存储过程的外面定义的.当调用存储过程的时候,必须要给他代入 5 --值.这种变量,SQL在编译的时候知道他的值是多少. 6 7 --例如: 8 USE [AdventureWorks] 9 GO 10 DROP PROC Sniff 11 GO 12 CREATE PROC Sniff(@i INT) 13 AS 14 SELECT CO

参数嗅探(Parameter Sniffing)(1/2)

这个问题会在参数话的SQL语句(例如存储过程)与SQL Server里的计划缓存机制结合的时候会出现.这个文章分为2个部分,第1部分会介绍下参数嗅探(Parameter Sniffing)的概况,第2部分我们介绍下如何解决这个问题. 什么是参数嗅探(Parameter Sniffing) 在SQL Server里当你执行参数话的SQL查询时,查询优化器会基于第一个提供的参数值编译执行计划.然后生成的执行计划在计划缓存里缓存作为后期的重用.这就是说SQL Server后续会直接重用这个计划,而不管

参数探测(Parameter Sniffing)影响存储过程执行效率

如果SQL query中有参数,SQL Server 会创建一个参数嗅探进程以提高执行性能.该计划通常是最好的并被保存以重复利用.只是偶尔,不会选择最优的执行计划而影响执行效率. SQL Server尝试通过创建编译执行计划来优化你的存储过程的执行.通常是在第一次执行存储过程时候会生成并缓存查询执行计划.当SQL Server数据库引擎编译存储过程中侦测到有参数值传递进来的时候,会创建基于这些参数的执行计划.这种在编译存储过程中侦测参数值的方法,通常被称为"参数探测".有时参数探测会产

Sql Server Parameter Sniffing 和 optimize for 介绍

Parameter sniffing是Sql Server 创建存储过程的执行计划时,根据传入的参数进行预估生成执行计划的一个功能,通俗的说,就是根据第一个参数为stored procedure生成执行计划,但是Stored Procedure的第一个参数产生的执行计划并不一定是最优的,当后续传参时重用执行计划时,原有的执行计划无法高效响应本次查询,导致查询性能低效. 对于参数嗅探导致的问题,可以使用重新编辑stored procedure,或使用optimize for hint来避免. Op

理解性能的奥秘——应用程序中慢,SSMS中快(4)——收集解决参数嗅探问题的信息

本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(3)--不总是参数嗅探的错 前面已经提到过关于存储过程在SSMS中运行很快,但在应用程序中运行很慢的可能原因:因为ARITHABORT的不同选项会导致不同的缓存词目,另外由于SQL Server使用了参数嗅探导致获得了不同的执行计划. 虽然已经说明了这个现象的原因,但是还没解释:如何定位和解决这个问题?到目前为止,大家都知道了如何快速处理,如果这个问题很紧急,可以直接使用: EX

请求(Request)的参数(Parameter)里包含特殊字符(#等)的正确处理方式

遇到一个问题 在一个地址链接(URL)里使用  url?param1=val1&param2=val2  的方式传递参数,结果在获取参数值时发现不是当初设定的值. 具体案例 以特殊字符井号(#)为例. 部分参数值丢了 JS里设置参数 window.location="some_web_project/xxx.jsp?param_key=abc#xyz"; Java中取参数值 Sting paramVal = request.getParameter("param_ke

SQL Server 查询优化(测试02)参数嗅探-执行计划选择

最近常看到"参数嗅探"这个词,看了几篇文章,于是就自己摸索做个测试来加深印象! 去官网下载了数据库:AdventureWorks2012 直接测试吧! 找几个熟悉的表关联起来,用ProductID作为条件找到两个ID返回行数相差较大的值. ProductID=870(4688行) ProductID=897(2行) [测试一] --先清空计划缓存 DBCC FREEPROCCACHE --执行前先打开计数器监控查看(分开执行以下查询) select sdh.SalesOrderID,s

PHPStorm2017去掉参数提示 parameter name hints

PHPStorm2017去掉参数提示 parameter name hints JetBrains 的各种语言的 IDE 都灰常灰常好用, 个个都是神器, PHPStorm 作为PHP开发的神器也不必多说了 今天升级到 PHPStorm 2017.1 发现增加了好些新功能, 有个默认开启的参数名和类型提示功能, 虽然功能挺强大的, 不过我用不着, 还是关掉的好, 有同样需求的同学可以看看 例子比较特殊这么看起来确实有点费眼睛的感觉还是关掉的好 配置面板中搜索 parameter name hin

理解性能的奥秘——应用程序中慢,SSMS中快(5)——案例:如何应对参数嗅探

本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(4)--收集解决参数嗅探问题的信息 首先我们需要明白,参数嗅探本身不是问题,而是一个特性,避免SQL Server做出盲目的假设,从而产生次优查询计划.但是有些情况下,参数嗅探却会带来负面影响.通常有下面三种典型的情况: 查询使用的参数嗅探完全不合适.也就是说,查询计划对于这次执行是合适的,但是对于下一次执行就可能不合适. 应用程序中存在特定的调用模式,而且与其他大部分调用模式差