何谓SQLSERVER参数嗅探问题

大家听到“嗅探”这个词应该会觉得跟黑客肯定有关系吧,使用工具嗅探一下参数,然后截获,脱裤o(∩_∩)o 。

事实上,我觉得大家太敏感了,其实这篇文章跟数据库安全没有什么关系,实际上跟数据库性能调优有关

相信大家有泡SQLSERVER论坛的话不多不少应该都会见过“参数嗅探”这几个字

这里有三篇帖子都是讲述参数嗅探的

http://social.msdn.microsoft.com/Forums/zh-CN/sqlserverzhchs/thread/caccb7f3-8366-4954-8f8a-145eb6bca9dd

http://msdn.microsoft.com/zh-cn/magazine/ee236412.aspx

http://social.msdn.microsoft.com/Forums/zh-CN/sqlserverzhchs/thread/bfbe54de-ac00-49e9-a83b-f97a60bf74ef

下面我给出一个测试数据库的备份文件,里面有一些表和一些测试数据 ,大家可以去下载,因为我下面用的测试表都是这个数据库里的

只需要还原数据库就可以了,这个数据库是SQL2005版本的,数据库名:AdventureWorks

下面只需要用到三张表,表里面有索引:

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

数据库下载链接:AdventureWorks

其实简单来讲,参数嗅探我的很通俗的解释就是: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

存储过程二:

 代码如下:

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] a11 INNER JOIN [dbo].[SalesOrderDetail_test] b12 ON a.[SalesOrderID]=b.[SalesOrderID]13 INNER JOIN [Production].[Product] p14 ON b.[ProductID]=p.[ProductID]15 WHERE a.[SalesOrderID]=@j16 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参数嗅探问题,希望对大家有所帮助!

(0)

相关推荐

  • 一些SQLServer存储过程参数及举例

    Microsoft included several hundred stored procedures in the various versions of Microsoft SQL Server and it has documented a good percentage of them. But many stored procedures remain undocumented. Some are used within the Enterprise Manager GUI in S

  • sqlserver 存储过程动态参数调用实现代码

    只是做笔记,没什么!! 复制代码 代码如下: --创建测试表 CREATE TABLE [dbo].[Student]( [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY, [Name] [nvarchar](20) NOT NULL DEFAULT (''), [Age] [int] NOT NULL DEFAULT (0), [Sex] [bit] NOT NULL DEFAULT (0), [Address] [nvarchar](200) NOT

  • SQLServer 参数化查询经验分享

    什么是参数化查询? 一个简单理解参数化查询的方式是把它看做只是一个T-SQL查询,它接受控制这个查询返回什么的参数.通过使用不同的参数,一个参数化查询返回不同的结果.要获得一个参数化查询,你需要以一种特定的方式来编写你的代码,或它需要满足一组特定的标准. 有两种不同的方式来创建参数化查询.第一个方式是让查询优化器自动地参数化你的查询.另一个方式是通过以一个特定方式来编写你的T-SQL代码,并将它传递给sp_executesql系统存储过程,从而编程一个参数化查询.这篇文章的后面部分将介绍这个方法

  • SqlServer参数化查询之where in和like实现详解

    身为一名小小的程序猿,在日常开发中不可以避免的要和where in和like打交道,在大多数情况下我们传的参数不多简单做下单引号.敏感字符转义之后就直接拼进了SQL,执行查询,搞定.若有一天你不可避免的需要提高SQL的查询性能,需要一次性where in 几百.上千.甚至上万条数据时,参数化查询将是必然进行的选择.然而如何实现where in和like的参数化查询,是个让不少人头疼的问题. where in 的参数化查询实现 首先说一下我们常用的办法,直接拼SQL实现,一般情况下都能满足需要 复

  • SqlServer参数化查询之where in和like实现之xml和DataTable传参介绍

    方案5 使用xml参数 对sql server xml类型参数不熟悉的童鞋需要先了解下XQuery概念,这里简单提下XQuery 是用来从 XML 文档查找和提取元素及属性的语言,简单说就是用于查询xml的语言说到这就会牵着到XPath,其实XPath是XQuery的一个子集,XQuery 1.0 和 XPath 2.0 共享相同的数据模型,并支持相同的函数和运算符,XPath的方法均适用于XQuery,假如您已经学习了 XPath,那么学习 XQuery 也不会有问题.详见http://www

  • 何谓SQLSERVER参数嗅探问题

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

  • SQLSERVER编译与重编译发生场景及重用的利弊介绍

    编译的含义 -------------------------------------------------------------------------------- 当SQLSERVER收到任何一个指令,包括查询(query).批处理(batch).存储过程.触发器(trigger) .预编译指令(prepared statement)和动态SQL语句(dynamic SQL Statement)要完成语法解释.语句解释, 然后再进行"编译(compile)",生成能够运行的&

  • SQL Server性能调优之缓存

    在执行任何查询时,SQL Server都会将数据读取到内存,数据使用之后,不会立即释放,而是会缓存在内存Buffer中,当再次执行相同的查询时,如果所需数据全部缓存在内存中,那么SQL Server不会产生Disk IO操作,立即返回查询结果,这是SQL Server的性能优化机制. 一,主要的内存消费者(Memory Consumer) 1,数据缓存(Data Cache) Data Cache是存储数据页(Data Page)的缓冲区,当SQL Server需要读取数据文件(File)中的数

  • 浅析SQL Server中的执行计划缓存(下)

    在上篇文章给大家介绍了SQL Server中的执行计划缓存(上),本文继续给大家介绍sqlserver执行计划缓存相关知识,小伙伴们一起学习吧. 简介 在上篇文章中我们谈到了查询优化器和执行计划缓存的关系,以及其二者之间的冲突.本篇文章中,我们会主要阐述执行计划缓存常见的问题以及一些解决办法. 将执行缓存考虑在内时的流程 上篇文章中提到了查询优化器解析语句的过程,当将计划缓存考虑在内时,首先需要查看计划缓存中是否已经有语句的缓存,如果没有,才会执行编译过程,如果存在则直接利用编译好的执行计划.因

  • sql server 编译与重编译详解

    SQLSERVER编译与重编译 编译的含义 当SQLSERVER收到任何一个指令,包括查询(query).批处理(batch).存储过程.触发器(trigger) .预编译指令(prepared statement)和动态SQL语句(dynamic SQL Statement)要完成语法解释.语句解释, 然后再进行"编译(compile)",生成能够运行的"执行计划(execution plan)".在编译的过程中, SQLSERVER会根据所涉及的对象的架构(sc

  • SQL Server统计信息更新时采样百分比对数据预估准确性的影响详解

    为什么要写统计信息 最近看到园子里有人写统计信息,楼主也来凑热闹. 话说经常做数据库的,尤其是做开发的或者优化的,统计信息造成的性能问题应该说是司空见惯. 当然解决办法也并非一成不变,"一招鲜吃遍天"的做法已经行不通了(题外话:整个时代不都是这样子吗) 当然,还是那句话,既然写了就不能太俗套,写点不一样的,本文通过分析一个类似实际案例来解读统计信息的更新的相关问题. 对于实际问题,不但要解决问题,更重要的是要从理论上深入分析,才能更好地驾驭数据库. 何时更新统计信息 (1)查询执行缓慢

  • Sql Server数据库各版本功能对比

    背景 今天举办的 Data Amp 大会上,微软向开发者们强调了 数据如何影响他们的应用和服务 ,顺道还宣布了几个小新闻.这个免费的线上研讨会不仅展示了未来的机器学习远景,还发布了 SQL Server 2017.推出了 面部认知服务 API 和 Azure 分析服务.显然,SQL Server 2017 将成为开发者们押注机器学习功能的下一个关键.其包含了对 Python(Python/R)的支持,且有能力承担数据密集型计算.微软开始向机器学习发力了. 数据库排名 从DB-Engines 发布

  • 浅析AngularJS中的生命周期和延迟处理

    这里,我们再讨论一些常用的高级的控制反转容器(Inversion of Control containers):延迟加载(lazy-loading),生命周期管理(lifetime management),以及延迟的创建/处理(deferred creation/resolution).   延迟加载(Lazy-Loading) 所谓延迟加载就是当你需要用到对象时候才对其进行实例化.许多依赖注入系统都会在一开始就创建组件,作为它的可依赖项目.不过有时候,直到在应用中用到它们之前,你都不会想去实例

  • SQL Server 2016 查询存储性能优化小结

    作为一个DBA,排除SQL Server问题是我们的职责之一,每个月都有很多人给我们带来各种不能解释却要解决的性能问题. 我就多次听到,以前的SQL Server的性能问题都还好且在正常范围内,但现在一切已经改变,SQL Server开始糟糕, 疯狂的事情不能解释.在这个情况下我介入,分析下整个SQL Server的安装,最后用一些神奇的调查方法找出性能问题的根源. 但很多时候问题的根源是一样的:所谓的计划回归(Plan Regression),即特定查询的执行计划已经改变.昨天SQL Serv

  • DataGridView使用自定义控件实现简单分页功能(推荐)

    本例子使用自定义控件方法实现,数据库使用的是SQL Server,实现过程如下: 1.新建一个自定义控件,命名为:PageControl. 2.PageControl代码如下: public partial class PageControl : UserControl { //委托及事件 public delegate void BindPage(int pageSize, int pageIndex, out int totalCount); public event BindPage Bi

随机推荐