MSSQL Server Parameter Sniffing Takip Procedure Oluşturma

Parameter Sniffing, SQL Server’da, bir stored procedure veya dinamik SQL sorgusu çalıştırıldığında kullanılan parametrelerin ilk değerine göre sorgu planının (execution plan) belirlenmesi olayıdır. Yani, SQL Server, ilk çalıştırılan parametre değerine dayanarak bir sorgu planı oluşturur ve bu plan sonrasında aynı prosedür veya sorgu başka parametrelerle çalıştırıldığında bu planı tekrar kullanır. Ancak, bazen bu durum, parametrelerin dağılımına bağlı olarak verimsiz bir sorgu planının oluşmasına sebep olabilir. Parameter Sniffing ile ilgili detaylı bilgi almak için parameter sniffing makalesi okunabilir.

Parameter Sniffing olayının yaşandığını veya sorgularda yaşana bilme ihtimalini takip edeceğimiz bir procedure yapısının oluşturulması gerekmektedir. Bunun için aşağıdaki yapıyla oluşturulabilir.

İlk olarak bir tablo oluşturulur.

CREATE TABLE [dbo].[LRQ](
[total_elapsed_s] [decimal](10, 5) NULL,
[statement] [varchar](8000) NULL,
[date] [datetime] NULL,
[loginname] [varchar](50) NULL,
[programname] [varchar](8000) NULL
) ON [PRIMARY]

Yukarıda tablo oluşturduktan sonra aşağıdaki procedure yardımıyla  tablomuzun içerisine parameter sniffing olan sorguları tablomuza kaydetmiş olacağız.

CREATE PROCEDURE [dbo].[sp_ParameterSniffing]  
AS  
BEGIN  
Declare @totalelapsed_s decimal(20,5),@plan_handle varbinary(64),@statement varchar(8000),@loginname varchar(50),@program varchar(8000),@wait_types varchar(150)  
dECLARE @TBL AS TABLE( [total_elapsed_s] [decimal](10, 5) NULL,  
    plan_handle varbinary (max),  
[statement] [varchar](max) NULL,  
[date] [datetime] NULL,  
[loginname] [varchar](100) NULL,  
[programname] [varchar](8000) NULL,
[wait_types] [varchar](150) NULL)  
INSERT INTO @TBL  
Select top 100    
  totalelapsed_s=r.total_elapsed_time / 1000.0  
 ,plan_handle=r.plan_handle  
 ,statement=SUBSTRING(t.text, (r.statement_start_offset/2) + 1,  ((CASE statement_end_offset         WHEN -1 THEN DATALENGTH(t.text)        ELSE r.statement_end_offset END            - r.statement_start_offset)/2) + 1)  
 ,[date]=null  
 ,loginname=s.login_name,program=s.program_name 
 ,r.last_wait_type
 from sys.dm_exec_requests r  
inner join sys.dm_exec_sessions s on r.session_id = s.session_id  
cross apply sys.dm_exec_sql_text(r.sql_handle) t  
cross apply sys.dm_exec_query_plan(plan_handle) as cp  
where s.program_name not like 'SQL Server Profiler%' AND s.program_name not like '%SQLAgent%' AND  r.session_id <> @@SPID  and r.blocking_session_id=0  
AND t.text NOT LIKE '%WAITFOR%'
AND t.text  NOT LIKE '%BACKUP%' and  t.text  NOT LIKE '%RESTORE%'
AND s.login_name not in('DINT\SunucuAdminleri',
'DINT\ZKSQL44$',
'DINT\vts00',
'DINT\VTS01',
'DINT\VTS02',
'DINT\VTS03',
'DINT\VTS04',
'DINT\VTS05',
'NT SERVICE\SQLAgent$NK2017',
'NT SERVICE\SQLWriter',
'NT SERVICE\Winmgmt',
'sa',
'sa00',
'NT AUTHORITY\SYSTEM',
'tasima'
)
aND r.total_elapsed_time / 1000.0>10
order by r.total_elapsed_time asc  
  
DECLARE db_cursor CURSOR FOR  
select [total_elapsed_s] ,plan_handle ,statement ,loginname ,[programname], wait_types from  @TBL  
OPEN db_cursor    
FETCH NEXT FROM db_cursor INTO @totalelapsed_s ,@plan_handle ,@statement ,@loginname ,@program ,@wait_types  
  
WHILE @@FETCH_STATUS = 0    
BEGIN    
 DBCC FREEPROCCACHE (@plan_handle);  
 INSERT INTO  [DBAMON].[dbo].[LRQ]  
 Select @totalelapsed_s,@statement,GETDATE(),@loginname,@program, @wait_types  
      FETCH NEXT FROM db_cursor INTO @totalelapsed_s ,@plan_handle ,@statement ,@loginname ,@program ,@wait_types  
END  
CLOSE db_cursor    
DEALLOCATE db_cursor  
END  
GO

Bu procedure özellikle parameter sniffing için değil, genel olarak performans sorunu yaratabilecek tüm uzun süren sorguları tespit etmek ve plan cache’ini temizlemek için kullanılır. Filtreler sadece Profiler ve SQLAgent gibi sistem sorgularını hariç tutuyor. Ayrıca pland handle silinen değeri bir tabloya kaydetmektedir.S

Eğer bu procedure’ü sadece parameter sniffing sorunlarını tespit etmek için kullanmak isterseniz, şu değişiklikleri yapabilirsiniz:

-- Mevcut WHERE koşullarına ek yapılabilir
WHERE ...
AND (t.text LIKE '%@%' OR t.text LIKE 'sp_%') -- Parametre veya stored proc arar
AND t.text NOT LIKE '%CREATE%' -- CREATE ifadelerini hariç tutar

Daha sonra bir job aracılığıyla sorgularımızı belirli aralıklarla çalıştırabiliriz.

DECLARE @tsql nvarchar(max)=''
SET @tsql = 'EXECUTE [DBAMON].[dbo].[sp_ParameterSniffing] '
DECLARE @Counter tinyint=1
While (@Counter<10)
BEGIN
exec (@tsql)
WAITFOR DELAY '00:00:05';
SET @Counter=@Counter+1
END

Daha sonra ilgili tablomuza sniffing yaşayan sorgularımızı görebiliriz.

  • total_elapsed_s: Sorgunun çalışma süresini saniye cinsinden gösterir (örneğin 50.186 saniye)
  • statement: Çalıştırılan SQL ifadesinin tipi (SELECT, UPDATE, INSERT, DELETE vb.)
  • date: Sorgunun çalıştırıldığı tarih ve saat
  • loginname: Sorguyu çalıştıran uygulama/kullanıcı
  • programme: Sorguyu çalıştıran program/istemci

Aşağıdaki komut yardımıyla sorgumuzun plan handle değerini bulabiliriz.

USE DB_NAME;
SELECT plan_handle,cp.usecounts,cp.objtype, st.text,DB_NAME(st.dbid) as [Database Name]
FROM sys.dm_exec_cached_plans as cp
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st 
WHERE  text LIKE N'Statement kısmındaki sorgumuz yazılmalıdır.%' OPTION (RECOMPILE);

Yukarıdaki kodumuzu ve dönen ifadeleri açıklamak gerekirse;

Hangi veritabanı üzerinde çalışacaksak ilgili veritabanının seçilmesi gerekmektedir.

plan_handle: Sorgu veya stored procedure yürütme planının benzersiz kimliği.
objtype: Planın türü. Örneğin, Proc stored procedure için, Adhoc ise doğrudan çalıştırılan sorgular içindir.
usecounts: İlgili sorgunun  kaç kez kullanıldığını gösterir.

Neden OPTION (RECOMPILE) Kullanılıyor?

Bu, sorgunun kendisinin önbelleğe alınmasını engeller ve daha doğru bir sonuç almanızı sağlar. Aksi halde, sorgunun planı da önbelleğe alınabilir ve yanlış ‘usecounts’ bilgisi gösterebilir.

Belirli bir sorgu planını temizlemek için:

DBCC FREEPROCCACHE(plan_handle);

Burada plan_handle, temizlenmek istenen sorgu planının kimliğidir.

Aşağıdaki ifade ile ilgili handle execution plan cache’den silinmektedir.

Yukarıda  yöntemler ve  son kısımda kod kısımları sorguların daha verimli çalışmasını sağlamak ve parameter sniffing’in olumsuz etkilerini ortadan kaldırmak için kullanılabilir.

Not: Procedure çalıştırıldıktan sonra execution planda bakıldığında default olarak hangi değerin kullanılıp kullanılmadığını anlarız.

Başka bir makalede görüşmek dileğiyle..

Sãd. Öğüt ve uyarı dolu o şerefli Kur’an’a yemin olsun ki, tek kurtuluş yolu İslâm yoludur! Sâd -1

Author: Yunus YÜCEL

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir