何谓SQL Server参数嗅探
大家听到“嗅探”这个词应该会觉得跟黑客肯定有关系吧,使用工具嗅探一下参数,然后截获,脱裤o(∩_∩)o 。
事实上,我觉得大家太敏感了,其实这篇文章跟数据库安全没有什么关系,实际上跟数据库性能调优有关
相信大家有泡SQLSERVER论坛的话不多不少应该都会见过“参数嗅探”这几个字
这里有三篇帖子都是讲述参数嗅探的
http://msdn.microsoft.com/zh-cn/magazine/ee236412.aspx
下面我给出一个测试数据库的备份文件,里面有一些表和一些测试数据 ,大家可以去下载,因为我下面用的测试表都是这个数据库里的
只需要还原数据库就可以了,这个数据库是SQL2005版本的,数据库名:AdventureWorks
下面只需要用到三张表,表里面有索引:
[Production].[Product]
[SalesOrderHeader_test]
[SalesOrderDetail_test]
数据库下载链接:AdventureWorks_Full_backup_2013-3-4.bak
其实简单来讲,参数嗅探我的很通俗的解释就是:SQLSERVER用鼻子嗅不到具体参数是多少
所以他不能选择最合适的执行计划去执行你的查询,所以参数嗅探是一个不好的现象。
想真正了解参数嗅探,大家可以先创建下面两个存储过程
存储过程一:

1 USE [AdventureWorks]
2 GO
3 DROP PROC Sniff
4 GO
5 CREATE PROC Sniff(@i INT)
6 AS
7 SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
8 FROM [dbo].[SalesOrderHeader_test] a
9 INNER JOIN [dbo].[SalesOrderDetail_test] b
10 ON a.[SalesOrderID]=b.[SalesOrderID]
11 INNER JOIN [Production].[Product] p
12 ON b.[ProductID]=p.[ProductID]
13 WHERE a.[SalesOrderID]=@i
14 GO

存储过程二:

1 USE [AdventureWorks]
2 GO
3 DROP PROC Sniff2
4 GO
5 CREATE PROC Sniff2(@i INT)
6 AS
7 DECLARE @j INT
8 SET @j=@i
9 SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
10 FROM [dbo].[SalesOrderHeader_test] a
11 INNER JOIN [dbo].[SalesOrderDetail_test] b
12 ON a.[SalesOrderID]=b.[SalesOrderID]
13 INNER JOIN [Production].[Product] p
14 ON b.[ProductID]=p.[ProductID]
15 WHERE a.[SalesOrderID]=@j
16 GO

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

1 --测试一:
2 USE [AdventureWorks]
3 GO
4 DBCC freeproccache
5 GO
6 EXEC [dbo].[Sniff] @i = 500000 -- int
7 --发生编译,插入一个使用nested loops联接的执行计划
8 GO
9
10 EXEC [dbo].[Sniff] @i = 75124 -- int
11 --发生执行计划重用,重用上面的nested loops的执行计划
12 GO

测试二:

1 --测试二:
2
3 USE [AdventureWorks]
4 GO
5 DBCC freeproccache
6 GO
7 SET STATISTICS PROFILE ON
8 EXEC [dbo].[Sniff] @i = 75124 -- int
9 --发生编译,插入一个使用hash match联接的执行计划
10 GO
11
12 EXEC [dbo].[Sniff] @i = 50000 -- int
13 --发生执行计划重用,重用上面的hash match的执行计划
14 GO

从上面两个测试可以清楚地看到执行计划重用的副作用。
由于数据分布差别很大参数50000和75124只对自己生成的执行计划有好的性能,
如果使用对方生成的执行计划,性能就会下降。参数50000返回的结果集比较小,
所以性能下降不太严重。参数75124返回的结果集大,就有了明显的性能下降,两个执行计划的差别有近10倍
对于这种因为重用他人生成的执行计划而导致的水土不服现象,SQSERVERL有一个专有名词,叫“参数嗅探 parameter sniffing”
因为语句的执行计划对变量的值很敏感,而导致重用执行计划会遇到性能问题,就是我上面说的
“
SQLSERVER用鼻子嗅不到具体参数是多少,所以他不能选择最合适的执行计划去执行你的查询
”
本地变量的影响
那对于有parameter sniffing问题的存储过程,如果使用本地变量,会怎样呢?
下面请看测试3。这次用不同的变量值时,都清空执行计划缓存,迫使其重编译

1 --第一次
2 USE [AdventureWorks]
3 GO
4 DBCC freeproccache
5 GO
6 SET STATISTICS TIME ON
7 SET STATISTICS PROFILE ON
8 EXEC [dbo].[Sniff] @i = 50000 -- int
9 GO


1 --第二次
2 USE [AdventureWorks]
3 GO
4 DBCC freeproccache
5 GO
6 SET STATISTICS TIME ON
7 SET STATISTICS PROFILE ON
8 EXEC [dbo].[Sniff] @i = 75124 -- int
9 GO


1 --第三次
2 USE [AdventureWorks]
3 GO
4 DBCC freeproccache
5 GO
6 SET STATISTICS TIME ON
7 SET STATISTICS PROFILE ON
8 EXEC [dbo].[Sniff2] @i = 50000 -- int
9 GO


1 --第四次
2 USE [AdventureWorks]
3 GO
4 DBCC freeproccache
5 GO
6 SET STATISTICS TIME ON
7 SET STATISTICS PROFILE ON
8 EXEC [dbo].[Sniff2] @i = 75124 -- int
9 GO

看他们的执行计划:
对于第一句和第二句,因为SQL在编译的时候知道变量的值,所以在做EstimateRows的时候,做得非常准确,选择了最适合他们的执行计划
但是对于第三句和第四句,SQLSERVER不知道@j的值是多少,所以在做EstimateRows的时候,不管代入的@i值是多少,
一律给@j一样的预测结果。所以两个执行计划是完全一样的(都是Hash Match)。
参数嗅探的解决办法
参数嗅探的问题发生的频率并不高,他只会发生在一些表格里的数据分布很不均匀,或者用户带入的参数值很不均匀的情况下。
由于篇幅原因我就不具体说了,只是做一些归纳
(1)用exec()的方式运行动态SQL
如果在存储过程里不是直接运行语句,而是把语句带上变量,生成一个字符串,再让exec()这样的命令做动态语句运行,
那SQL就会在运行到这句话的时候,对动态语句进行编译。
这时SQL已经知道了变量的值,会根据生成优化的执行计划,从而绕过参数嗅探问题

1 --例如前面的存储过程Sniff,就可以改成这样
2 USE [AdventureWorks]
3 GO
4 DROP PROC NOSniff
5 GO
6 CREATE PROC NOSniff(@i INT)
7 AS
8 DECLARE @cmd VARCHAR(1000)
9 SET @cmd='SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
10 FROM [dbo].[SalesOrderHeader_test] a
11 INNER JOIN [dbo].[SalesOrderDetail_test] b
12 ON a.[SalesOrderID]=b.[SalesOrderID]
13 INNER JOIN [Production].[Product] p
14 ON b.[ProductID]=p.[ProductID]
15 WHERE a.[SalesOrderID]='
16 EXEC(@cmd+@i)
17 GO

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

1 USE [AdventureWorks]
2 GO
3 DROP PROC NoSniff_QueryHint_Recompile
4 GO
5 CREATE PROC NoSniff_QueryHint_Recompile(@i INT)
6 AS
7 SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
8 FROM [dbo].[SalesOrderHeader_test] a
9 INNER JOIN [dbo].[SalesOrderDetail_test] b
10 ON a.[SalesOrderID]=b.[SalesOrderID]
11 INNER JOIN [Production].[Product] p
12 ON b.[ProductID]=p.[ProductID]
13 WHERE a.[SalesOrderID]=@i
14 OPTION(RECOMPILE)
15 GO

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

1 USE [AdventureWorks]
2 GO
3 EXEC [sys].[sp_create_plan_guide]
4 @name=N'Guide1',
5 @stmt=N'SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
6 FROM [dbo].[SalesOrderHeader_test] a
7 INNER JOIN [dbo].[SalesOrderDetail_test] b
8 ON a.[SalesOrderID]=b.[SalesOrderID]
9 INNER JOIN [Production].[Product] p
10 ON b.[ProductID]=p.[ProductID]
11 WHERE a.[SalesOrderID]=@i',
12 @type=N'OBJECT',
13 @module_or_batch=N'Sniff',
14 @params=NULL,
15 @hints=N'option(optimize for(@i=75124))';
16 GO

对于Plan Guide,他还可以使用在一般的语句调优里
转自:https://www.cnblogs.com/lyhabc/archive/2013/03/02/2941144.html
何谓SQL Server参数嗅探的更多相关文章
- SQL Server 参数嗅探问题
摘要 MSSQL Server参数嗅探既是一个涉及知识面非常广泛,又是一个比较难于解决的课题,即使对于数据库老手也是一个比较头痛的问题.这篇文章从参数嗅探是什么,如何产生,表象是什么,会带来哪些问题, ...
- C#调用SQL Server参数过程传参
-SQL SERVER生成测试环境: Create database Test; go USE [Test] GO if OBJECT_ID('Tab2','U') is not null drop ...
- SQL Server @@参数一览表
--返回 SQL Server 自上次启动以来尝试的连接数,无论连接是成功还是失败. SELECT @@CONNECTIONS AS CONNECTIONS --返回 SQL Server 自上次启动 ...
- SQL Server参数优化
内存参数: 此处为512G物理内存,一般来说设置为物理内存80%左右,设置过大资源不会自动释放,内存可能会持续增长:设置过小资源浪费. CPU参数: 最大工作线程数: 此处为4个CPU40核,调整后物 ...
- SQL Server - 最佳实践 - 参数嗅探问题 转。
文章来自:https://yq.aliyun.com/articles/61767 先说我的问题,最近某个存储过程,暂定名字:sp_a 总是执行超时,sp_a带有一个参数,暂定名为 para1 var ...
- 理解性能的奥秘——应用程序中慢,SSMS中快(2)——SQL Server如何编译存储过程
本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(1)--简介 本文介绍SQL Server如何编译存储过程并使用计划缓存 ...
- Oracle导数据到SQL server的方法总结
通过oracle10g 访问sql server 2008 导数据步骤 最近在项目中遇到要将Oracle数据库的数据导入到SQL server数据库中,解决办法如下: 一.准备工作 配置Oracle ...
- 在sql server中建存储过程,如果需要参数是一个可变集合怎么处理?
在sql server中建存储过程,如果需要参数是一个可变集合的处理 原存储过程,@objectIds 为可变参数,比如 110,98,99 ALTER PROC [dbo].[Proc_totalS ...
- SQL SERVER使用ODBC 驱动建立的链接服务器调用存储过程时参数不能为NULL值
我们知道SQL SERVER建立链接服务器(Linked Server)可以选择的驱动程序非常多,最近发现使用ODBC 的 Microsoft OLE DB 驱动程序建立的链接服务器(Linked S ...
随机推荐
- 作业20171123 beta-review 成绩
申诉 对成绩有疑问或不同意见的同学,请在群里[@杨贵福]. 申诉时间截止2017年12月13日 17:00. 成绩 review NABCD-评论 SPEC-评论 bug found 答复 bugfi ...
- 第七周 linux如何装载和启动一个可执行文件
潘恒 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验内容 1.预处理. ...
- action中session的存取
存 ActionContext.getContext().getSession().put("teacherlist", teacherlist); 取 teacherlist=( ...
- 阅读<构建之法>13、14、15、16、17章
13章 这么多测试为什么不能整理出一个包括所有功能的测试呢?看着那么多测试都感觉奇怪了. 14章 怎样才能体现一个测试人员的工作价值呢?这样的判断又是否会太独断了? 15章 在时间上,会不会因不同功能 ...
- JS对象复制(深拷贝、浅拷贝)
如何在 JS 中复制对象 在本文中,我们将从浅拷贝(shallow copy)和深拷贝(deep copy)两个方面,介绍多种 JS 中复制对象的方法. 在开始之前,有一些基础知识值得一提:Javas ...
- eclipse repository connector
- 关于js中this指向的理解总结!
关于js中this指向的理解! this是什么?定义:this是包含它的函数作为方法被调用时所属的对象. 首先,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁 ...
- [读书笔记]SQLSERVER企业级平台管理实践读书笔记02
记录一下 这一块 join的理解了 再完善过来. 1. Statistics的用法: 清空执行计划用的命令 dbcc freeproccache 清空buffer pool 里面的缓存命令 dbcc ...
- 解决主机ping不通虚拟机
vmware 08网卡 解决的办法就是 从vmware中,选择菜单:edit->virtual network editor,选择nat,点击左下角的”restore Default“,然后就正 ...
- python逻辑回归 自动建模
#-*- coding: utf-8 -*- #逻辑回归 自动建模 import numpy as np import pandas as pd from sklearn.linear_model i ...