Monday, April 30, 2007

Parameter Sniffing

This blog deals with below:

·        Understanding Parameter Sniffing.

·        Example

·        Possible Solutions with application for each one of them.

The query fired against SQL Server, is first compiled before execution. Subsequent executes of stored procedure reuse already stored execution plans. This is plan reuse helps saving CPU cycles in compilation thus increasing throughput. The reuse of plan is enhanced using stored procedures.  In most of the cases using saved execution plan is good but in some cases the plan reuse may lead to inconsistent performance problems specially in Stored procedures. Parameter sniffing occurring typically when data is skewed (that is values of one type are more frequent then other values).

When a query is executed, based on statistical information about underlying tables accessed by query, SQL Server will generate execution plans.. Execution plan is set of logical steps that sql server will follow to generate results. The parameters being passed to stored procedure are compared against statistics present for tables / indexes (Statistics indicate distribution of data).

Say for example in Employee table we have column Gender where there are 1000 Males but 10 Females that is data being skewed. Below is a very sample script to create understand parameter sniffing.

use tempdb

go

if exists(select 1 from sysobjects where name like 'Employee')

begin

          drop table Employee

end

create table Employee(id int, name varchar(100) , Gender Char(1))

go

declare @i int

set @i = 1

while @i < 100000

begin

          insert into Employee values(@i,'Guru','M')

          set @i = @i +1

end

go

declare @i int

set @i = 100000

while @i < 100056

begin

          insert into Employee values(@i,'Saru','F')

          set @i = @i +1

end

go

 

go

create clustered index ind on Employee(id)

go

create  index ind1 on Employee(Gender)

go

create procedure USP_Parameter_Sniffing

@Gender char(1)

as

begin

select * from Employee where Gender = @Gender

end

go

set statistics profile on

set statistics IO on

set statistics time on

go

          USP_Parameter_Sniffing 'F'

go

set statistics profile on

set statistics IO on

set statistics time on

go

For Above query with ‘F’ as parameter, apart from query results below execution plan is used to produce results. The execution plan contains Non clustered indexes that point to Clustered index keys and use Bookmark lookup operator for selecting rows from base tables.

Rows        Executes    StmtText                                                                                                             

----------- ----------- ----------------------------------------------------------------------------------------------------------------------

56          1           select * from Employee where Gender = @Gender                                                                         

56          1             |--Bookmark Lookup(BOOKMARK:([Bmk1000]), OBJECT:([tempdb].[dbo].[Employee]) WITH PREFETCH)                         

56          1                  |--Index Seek(OBJECT:([tempdb].[dbo].[Employee].[ind1]), SEEK:([Employee].[Gender]=[@Gender]) ORDERED FORWARD)

Now if we run the same query with ‘M’ Parameter..

set statistics profile on

set statistics IO on

set statistics time on

go

          USP_Parameter_Sniffing 'M'

go

set statistics profile on

set statistics IO on

set statistics time on

go

SQL Server Execution Times:

   CPU time = 94 ms,  elapsed time = 25000 ms.

Rows        Executes    StmtText                                                                                                              

----------- ----------- ----------------------------------------------------------------------------------------------------------------------

99999       1           select * from Employee where Gender = @Gender                                                                         

99999        1             |--Bookmark Lookup(BOOKMARK:([Bmk1000]), OBJECT:([tempdb].[dbo].[Employee]) WITH PREFETCH)                          

99999        1                  |--Index Seek(OBJECT:([tempdb].[dbo].[Employee].[ind1]), SEEK:([Employee].[Gender]=[@Gender]) ORDERED FORWARD) 

SQL Server Execution Times:

   CPU time = 94 ms,  elapsed time = 25000 ms.

SQL Server Execution Times:

   CPU time = 94 ms,  elapsed time = 25000 ms.

But if you run below statement you would get results faster and you would see it would go for clustered index scan instead of Non clustered index scan followed by book mark lookup

dbcc freeproccache

set statistics profile on

set statistics IO on

set statistics time on

go

          USP_Parameter_Sniffing 'M'

go

set statistics profile on

set statistics IO on

set statistics time on

go

To understand more about Parameter sniffing:

http://blogs.msdn.com/queryoptteam/archive/2006/03/31/565991.aspx

How to find out if this phenomena is occurring on server?

Typical symptoms would include:

·        Assuming blocking and other resources are ruled out , if procedure execution times varying depending on parameters passed but execution plan remains same.

·        Data is skewed (more data of one type is present when compared to other like Male/Female in our example).

·        If procedure is executed with different parameters by running DBCC Freeproccache and each execute produces different plan then may be parameter sniffing is occurring.

o   Exec <procecure name> Parameter

o   DBCC FREEPROCCACHE

o   Exec <procecure name> Parameter1

Solutions would be:

·        If compilation cost of query is low, then sp_recompile could be used (If used incorrectly this could lead to HIGH CPU , so be careful about it).

·        Declare a second set of parameters inside stored procedure and use them in Where conditions.

·         DBCC FREEPROCCACHE would also work (But this will make all queries compile again and is not recommended)

In case you have questions please post them for reply.

 

Thanks & Regards

Guru Charan B

No comments: