大家听到“嗅探”这个词应该会觉得跟黑客肯定有关系吧,使用工具嗅探一下参数,然后截获,脱裤o(∩_∩)o 。
事实上,我觉得大家太敏感了,其实这篇文章跟数据库安全没有什么关系,实际上跟数据库性能调优有关
相信大家有泡SQLSERVER论坛的话不多不少应该都会见过“参数嗅探”这几个字
这里有三篇帖子都是讲述参数嗅探的:

SQL写法导致的SQL2008性能问题

Under the Table - How Data Access Code Affects Database Performance

困扰多时的查询语句超时问题


下面我给出一个测试数据库的备份文件,里面有一些表和一些测试数据 ,大家可以去下载,因为我下面用的测试表都是这个数据库里的
只需要还原数据库就可以了,这个数据库是SQL2005版本的,数据库名:AdventureWorks
下面只需要用到三张表,表里面有索引:

[Production].[Product]
[SalesOrderHeader_test]
[SalesOrderDetail_test]

其实简单来讲,参数嗅探我的很通俗的解释就是:SQLSERVER用鼻子嗅不到具体参数是多少
所以他不能选择最合适的执行计划去执行你的查询,所以参数嗅探是一个不好的现象。


想真正了解参数嗅探,大家可以先创建下面两个存储过程
存储过程一:

USE [AdventureWorks]
GO
DROP PROC Sniff
GO
CREATE PROC Sniff(@i INT)
AS
SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
FROM [dbo].[SalesOrderHeader_test] a
INNER JOIN [dbo].[SalesOrderDetail_test] b
ON a.[SalesOrderID]=b.[SalesOrderID]
INNER JOIN [Production].[Product] p
ON b.[ProductID]=p.[ProductID]
WHERE a.[SalesOrderID]=@i
GO

存储过程二:

USE [AdventureWorks]
GO
DROP PROC Sniff2
GO
CREATE PROC Sniff2(@i INT)
AS
DECLARE @j INT
SET @j=@i
SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
FROM [dbo].[SalesOrderHeader_test] a
INNER JOIN [dbo].[SalesOrderDetail_test] b
ON a.[SalesOrderID]=b.[SalesOrderID]
INNER JOIN [Production].[Product] p
ON b.[ProductID]=p.[ProductID]
WHERE a.[SalesOrderID]=@j
GO

然后请做下面这两个测试
测试一:

--测试一:
USE [AdventureWorks]
GO
DBCC freeproccache
GO
EXEC [dbo].[Sniff] @i = 500000 -- int
--发生编译,插入一个使用nested loops联接的执行计划
GO EXEC [dbo].[Sniff] @i = 75124 -- int
--发生执行计划重用,重用上面的nested loops的执行计划
GO

测试二:

--测试二:

USE [AdventureWorks]
GO
DBCC freeproccache
GO
SET STATISTICS PROFILE ON
EXEC [dbo].[Sniff] @i = 75124 -- int
--发生编译,插入一个使用hash match联接的执行计划
GO EXEC [dbo].[Sniff] @i = 50000 -- int
--发生执行计划重用,重用上面的hash match的执行计划
GO

从上面两个测试可以清楚地看到执行计划重用的副作用。
由于数据分布差别很大参数50000和75124只对自己生成的执行计划有好的性能,
如果使用对方生成的执行计划,性能就会下降。参数50000返回的结果集比较小,
所以性能下降不太严重。参数75124返回的结果集大,就有了明显的性能下降,两个执行计划的差别有近10倍


对于这种因为重用他人生成的执行计划而导致的水土不服现象,SQSERVERL有一个专有名词,叫“参数嗅探 parameter sniffing”
因为语句的执行计划对变量的值很敏感,而导致重用执行计划会遇到性能问题,就是我上面说的
“SQLSERVER用鼻子嗅不到具体参数是多少,所以他不能选择最合适的执行计划去执行你的查询”


本地变量的影响
那对于有parameter sniffing问题的存储过程,如果使用本地变量,会怎样呢?
下面请看测试3。这次用不同的变量值时,都清空执行计划缓存,迫使其重编译

--第一次
USE [AdventureWorks]
GO
DBCC freeproccache
GO
SET STATISTICS TIME ON
SET STATISTICS PROFILE ON
EXEC [dbo].[Sniff] @i = 50000 -- int
GO

--第二次
USE [AdventureWorks]
GO
DBCC freeproccache
GO
SET STATISTICS TIME ON
SET STATISTICS PROFILE ON
EXEC [dbo].[Sniff] @i = 75124 -- int
GO

--第三次
USE [AdventureWorks]
GO
DBCC freeproccache
GO
SET STATISTICS TIME ON
SET STATISTICS PROFILE ON
EXEC [dbo].[Sniff2] @i = 50000 -- int
GO

--第四次
USE [AdventureWorks]
GO
DBCC freeproccache
GO
SET STATISTICS TIME ON
SET STATISTICS PROFILE ON
EXEC [dbo].[Sniff2] @i = 75124 -- int
GO

看他们的执行计划:

对于第一句和第二句,因为SQL在编译的时候知道变量的值,所以在做EstimateRows的时候,做得非常准确,选择了最适合他们的执行计划
但是对于第三句和第四句,SQLSERVER不知道@j的值是多少,所以在做EstimateRows的时候,不管代入的@i值是多少,
一律给@j一样的预测结果。所以两个执行计划是完全一样的(都是Hash Match)。


参数嗅探的解决办法

参数嗅探的问题发生的频率并不高,他只会发生在一些表格里的数据分布很不均匀,或者用户带入的参数值很不均匀的情况下。
由于篇幅原因我就不具体说了,只是做一些归纳

(1)用exec()的方式运行动态SQL
如果在存储过程里不是直接运行语句,而是把语句带上变量,生成一个字符串,再让exec()这样的命令做动态语句运行,
那SQL就会在运行到这句话的时候,对动态语句进行编译。
这时SQL已经知道了变量的值,会根据生成优化的执行计划,从而绕过参数嗅探问题

--例如前面的存储过程Sniff,就可以改成这样
USE [AdventureWorks]
GO
DROP PROC NOSniff
GO
CREATE PROC NOSniff(@i INT)
AS
DECLARE @cmd VARCHAR(1000)
SET @cmd='SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
FROM [dbo].[SalesOrderHeader_test] a
INNER JOIN [dbo].[SalesOrderDetail_test] b
ON a.[SalesOrderID]=b.[SalesOrderID]
INNER JOIN [Production].[Product] p
ON b.[ProductID]=p.[ProductID]
WHERE a.[SalesOrderID]='
EXEC(@cmd+@i)
GO

(2)使用本地变量local variable

(3)在语句里使用query hint,指定执行计划
在select,insert,update,delete语句的最后,可以加一个"option(<query_hint>)"的子句
对SQLSERVER将要生成的执行计划进行指导。当DBA知道问题所在以后,可以通过加hint的方式,引导
SQL生成一个比较安全的,对所有可能的变量值都不差的执行计划

USE [AdventureWorks]
GO
DROP PROC NoSniff_QueryHint_Recompile
GO
CREATE PROC NoSniff_QueryHint_Recompile(@i INT)
AS
SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
FROM [dbo].[SalesOrderHeader_test] a
INNER JOIN [dbo].[SalesOrderDetail_test] b
ON a.[SalesOrderID]=b.[SalesOrderID]
INNER JOIN [Production].[Product] p
ON b.[ProductID]=p.[ProductID]
WHERE a.[SalesOrderID]=@i
OPTION(RECOMPILE)
GO

(4)Plan Guide
可以用下面的方法,在原来那个有参数嗅探问题的存储过程“Sniff”上,解决sniffing问题

USE [AdventureWorks]
GO
EXEC [sys].[sp_create_plan_guide]
@name=N'Guide1',
@stmt=N'SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
FROM [dbo].[SalesOrderHeader_test] a
INNER JOIN [dbo].[SalesOrderDetail_test] b
ON a.[SalesOrderID]=b.[SalesOrderID]
INNER JOIN [Production].[Product] p
ON b.[ProductID]=p.[ProductID]
WHERE a.[SalesOrderID]=@i',
@type=N'OBJECT',
@module_or_batch=N'Sniff',
@params=NULL,
@hints=N'option(optimize for(@i=75124))';
GO

对于Plan Guide,他还可以使用在一般的语句调优里

原文链接

何谓SQLSERVER参数嗅探(转载)的更多相关文章

  1. 偶遇 sqlserver 参数嗅探

    需求: 费用统计 环境: 查询设计多张大表 解决方案: 优化查询语句,封装成存储过程,建立索引,最终查询速度很不错.部署上线,告一段落... 一段时间后投诉来了... 客户投诉说查询没内容,我看了日志 ...

  2. SqlServer 查询的时候过滤条件有参数导致速度很慢的问题-参数嗅探

    何谓SQLSERVER参数嗅探 大家听到“嗅探”这个词应该会觉得跟黑客肯定有关系吧,使用工具嗅探一下参数,然后截获,脱裤o(∩_∩)o . 事实上,我觉得大家太敏感了,其实这篇文章跟数据库安全没有什么 ...

  3. 何谓SQL Server参数嗅探

    大家听到"嗅探"这个词应该会觉得跟黑客肯定有关系吧,使用工具嗅探一下参数,然后截获,脱裤o(∩_∩)o . 事实上,我觉得大家太敏感了,其实这篇文章跟数据库安全没有什么关系,实际上 ...

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

    这个问题会在参数话的SQL语句(例如存储过程)与SQL Server里的计划缓存机制结合的时候会出现.这个文章分为2个部分,第1部分会介绍下参数嗅探(Parameter Sniffing)的概况,第2 ...

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

    在参数嗅探(Parameter Sniffing)(1/2)里,我介绍了SQL Server里参数嗅探的基本概念和背后的问题.如你所见,当缓存的计划被SQL Server盲目重用时,会带来严重的性能问 ...

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

    本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(4)--收集解决参数嗅探问题的信息 首先我们需要明白,参数嗅探本身不是问 ...

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

    本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(3)--不总是参数嗅探的错 前面已经提到过关于存储过程在SSMS中运行很 ...

  8. 理解性能的奥秘——应用程序中慢,SSMS中快(3)——不总是参数嗅探的错

    本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(2)--SQL Server如何编译存储过程 在我们开始深入研究如何处理 ...

  9. SQL Server - 最佳实践 - 参数嗅探问题 转。

    文章来自:https://yq.aliyun.com/articles/61767 先说我的问题,最近某个存储过程,暂定名字:sp_a 总是执行超时,sp_a带有一个参数,暂定名为 para1 var ...

随机推荐

  1. qt 界面去掉系统边框

    该代码在Qt5框架编辑,使用该类时, 直接继承这个类就可以了. 实现了拖拽功能和关闭功能,如果需要放大缩小功能, 需自己实现. 1 #ifndef CUSTOMIZE_QWIDGET_H #defin ...

  2. Spring Boot 中如何支持异步方法

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  3. Rpg maker mv角色扮演游戏制作大师简介

    目录 1:简介 2:基本图片展示 3.和js等平台的合作 @(这里写自定义目录标题) 1:简介   <RPG制作大师MV>为<RPG制作大师>的新版本,于18年11月27日登陆 ...

  4. 在Vue-cli3.x中引入element-ui的新方式

    今天登上element官网,发现element对vue-cli3.x的项目做了特殊定制: 这意味着即使是按需引入,也无需像过去那样手动安装babel-plugin-component,配置babel. ...

  5. github pages与travis ci运作原理

    当说到自动部署的时候,我很反感那些一上来就balabala说怎么操作的博文文章,照着别人的做法有样学样,经常会因为与自己项目实际情况不符而出现各种问题. 比如说github和travis,首先应该搞明 ...

  6. 并发—JVM内部机制和外部机制处理方法

    并发常见的编程场景,一句话概括就是,需要协调多个线程之间的协作,已保证程序按照自己原本的意愿执行.那么究竟应该如何协调多个线程? 这个问题比较宽泛,一般情况下,我们按照方式的纬度去简单区分,有以下两种 ...

  7. django4-模板进阶

    1.模板系统的语法 引用变量数据: {{  }} 标签逻辑操作:{%...%} 2.变量 变量名由字符数字下划线组成 ,可以执行由视图函数传过来的对象的方法和属性 ,但是视图函数中如果使用 ' . ' ...

  8. HTTP中的301、302、303、307、308

    结论 3XX开头的HTTP状态码都表示重定向的响应. 301.308是永久重定向:302.303.307是临时重定向. 301.302是http 1.0的内容,303.307.308是http1.1的 ...

  9. 论文学习-wlg-基于二维材料的肖特基异质结构的通用尺度定律

    目录 主要公式: 各个段落的内容 第一页 第二页 第三页 名词的含义 功函数: 电子亲和力 肖特基势垒 肖特基二极管的原理 非相对论性电子气:未知 Rashba自旋电子系统: 参考链接: 主要公式: ...

  10. RabbitMQ获取队列的消息数目

    使用RabbitMQ,业务需求,想要知道队列中还有多少待消费待数据. 方式一: @Value("${spring.rabbitmq.host}") private String h ...