Pengantar
Pengembang sering kali diminta untuk menggunakan prosedur tersimpan untuk menghindari apa yang disebut kueri ad hoc yang dapat mengakibatkan kembung yang tidak perlu dari cache rencana. Anda lihat, ketika kode SQL berulang ditulis secara tidak konsisten atau ketika ada kode yang menghasilkan SQL dinamis dengan cepat, SQL Server memiliki kecenderungan untuk membuat rencana eksekusi untuk setiap eksekusi individu. Hal ini dapat menurunkan kinerja secara keseluruhan sebesar:
Menuntut fase kompilasi untuk setiap eksekusi kode.
Membengkak Cache Rencana dengan terlalu banyak pegangan rencana yang tidak dapat digunakan kembali.
Optimalkan untuk Beban Kerja Ad Hoc
Salah satu cara mengatasi masalah ini di masa lalu adalah Mengoptimalkan instans untuk Beban Kerja Ad Hoc. Melakukan hal ini hanya dapat membantu jika sebagian besar database atau database paling signifikan pada instans sebagian besar menjalankan Ad Hoc SQL.
Gbr. 1 Optimalkan untuk Beban Kerja Ad Hoc
--Enable OFAW Using T-SQL EXEC sys.sp_configure N'show advanced options', N'1' RECONFIGURE WITH OVERRIDE GO EXEC sys.sp_configure N'optimize for ad hoc workloads', N'1' GO RECONFIGURE WITH OVERRIDE GO EXEC sys.sp_configure N'show advanced options', N'0' RECONFIGURE WITH OVERRIDE GO
Pada dasarnya, opsi ini memberi tahu SQL Server untuk menyimpan versi sebagian dari paket yang dikenal sebagai rintisan paket terkompilasi. Rintisan menempati ruang yang jauh lebih sedikit daripada keseluruhan denah.
Sebagai alternatif dari metode ini, beberapa orang mendekati masalah dengan agak brutal dan sesekali membersihkan cache paket. Atau, dengan cara yang lebih hati-hati, siram "paket sekali pakai" dengan menggunakan DBCC FREESYSTEMCACHE. Membersihkan seluruh cache paket memiliki kelemahan, seperti yang mungkin sudah Anda ketahui.
Menggunakan Prosedur dan Parameter Tersimpan
Dengan menggunakan prosedur tersimpan, seseorang dapat secara virtual menghilangkan masalah yang disebabkan oleh SQL Ad Hoc. Prosedur tersimpan dikompilasi hanya sekali dan rencana yang sama digunakan kembali untuk eksekusi selanjutnya dari kueri SQL yang sama atau serupa. Ketika prosedur tersimpan digunakan untuk mengimplementasikan logika bisnis, perbedaan utama dalam kueri SQL yang akhirnya akan dieksekusi oleh SQL Server terletak pada parameter yang diteruskan pada waktu eksekusi. Karena paket sudah ada dan siap digunakan, SQL Server akan menggunakan paket yang sama tidak peduli parameter apa yang diteruskan.
Data Miring
Dalam skenario tertentu, data yang kita hadapi tidak terdistribusi secara merata. Kita dapat mendemonstrasikan ini – pertama, kita perlu membuat tabel:
--Create Table with Skewed Data use Practice2017 go create table Skewed ( ID int identity (1,1) , FirstName varchar(50) , LastName varchar(50) , CountryCode char(2) ); insert into Skewed values ('Kwaku','Amoako','GH') go 10000 insert into Skewed values ('Kenneth','Igiri','NG') go 10 insert into Skewed values ('Steve','Jones','US') go 2 create clustered index CIX_ID on Skewed(ID); create index IX_CountryCode on Skewed (CountryCode);
Tabel kami berisi data anggota klub dari berbagai negara. Sejumlah besar anggota klub berasal dari Ghana, sementara dua negara lain masing-masing memiliki sepuluh dan dua anggota. Untuk tetap fokus pada agenda dan demi kesederhanaan, saya hanya menggunakan tiga negara dan nama yang sama untuk anggota yang berasal dari negara yang sama. Juga, saya menambahkan indeks berkerumun di kolom ID dan indeks tidak berkerumun di kolom Kode Negara untuk menunjukkan efek dari rencana eksekusi yang berbeda untuk nilai yang berbeda.
Gbr. 2 Rencana eksekusi untuk dua kueri
Saat kami menanyakan tabel untuk catatan di mana CountryCode adalah NG dan GH, kami menemukan bahwa SQL Server menggunakan dua rencana eksekusi yang berbeda dalam kasus ini. Ini terjadi karena jumlah baris yang diharapkan untuk CountryCode='NG' adalah 10, sedangkan untuk CountryCode='GH' adalah 10.000. SQL Server menentukan rencana eksekusi yang lebih baik berdasarkan statistik tabel. Jika jumlah baris yang diharapkan tinggi dibandingkan dengan jumlah total baris dalam tabel, SQL Server memutuskan bahwa lebih baik melakukan pemindaian tabel penuh daripada merujuk ke indeks. Dengan perkiraan jumlah baris yang jauh lebih kecil, indeks menjadi berguna.
Gbr. 3 Perkiraan jumlah baris untuk CountryCode='NG'
Gbr. 4 Perkiraan jumlah Baris untuk CountryCode='GH'
Masukkan Prosedur Tersimpan
Kita dapat membuat prosedur tersimpan untuk mengambil record yang kita inginkan dengan menggunakan kueri yang sama. Satu-satunya perbedaan kali ini adalah kita melewati Kode Negara sebagai parameter (lihat Daftar 3). Saat melakukan ini, kami menemukan bahwa rencana eksekusi sama tidak peduli parameter apa yang kami lewati. Rencana eksekusi yang akan digunakan ditentukan oleh rencana eksekusi yang dikembalikan saat pertama kali prosedur tersimpan dipanggil. Misalnya, jika kita menjalankan prosedur dengan CountryCode='GH' terlebih dahulu, maka akan menggunakan pemindaian tabel penuh sejak saat itu. Jika kita kemudian menghapus cache prosedur dan menjalankan prosedur dengan CountryCode='NG' terlebih dahulu, itu akan menggunakan pemindaian berbasis indeks di masa mendatang.
--Create a Stored Procedure to Fetch the Data use Practice2017 go select * from Skewed where CountryCode='NG'; select * from Skewed where CountryCode='GH'; create procedure FetchMembers ( @countrycode char(2) ) as begin select * from Skewed where [email protected] end; exec FetchMembers 'NG'; exec FetchMembers 'GH'; DBCC FREEPROCCACHE exec FetchMembers 'GH'; exec FetchMembers 'NG';
Gbr. 5 Rencana eksekusi pencarian indeks ketika 'NG' digunakan pertama kali
Gbr. 6 Rencana eksekusi pemindaian indeks berkerumun saat 'GH' digunakan terlebih dahulu
Eksekusi prosedur tersimpan berperilaku seperti yang dirancang – rencana eksekusi yang diperlukan digunakan secara konsisten. Namun, ini bisa menjadi masalah karena satu rencana eksekusi tidak cocok untuk semua kueri jika datanya miring. Menggunakan indeks untuk mengambil kumpulan baris yang hampir sebesar seluruh tabel tidak efisien – juga tidak menggunakan pemindaian penuh untuk mengambil hanya sejumlah kecil baris. Ini adalah masalah Pengendapan Parameter.
Solusi yang Mungkin
Salah satu cara umum untuk mengelola masalah Parameter Sniffing adalah dengan sengaja meminta kompilasi ulang setiap kali prosedur tersimpan dijalankan. Ini jauh lebih baik daripada membilas Cache Rencana – kecuali jika Anda ingin menghapus cache dari kueri SQL khusus ini, yang sepenuhnya mungkin. Lihatlah versi terbaru dari prosedur tersimpan. Kali ini, ia menggunakan OPTION (RECOMPILE) untuk mengelola masalah. Gbr.6 menunjukkan kepada kita bahwa, setiap kali stored procedure baru dieksekusi, ia menggunakan rencana yang sesuai dengan parameter yang kita lewati.
--Create a New Stored Procedure to Fetch the Data create procedure FetchMembers_Recompile ( @countrycode char(2) ) as begin select * from Skewed where [email protected] OPTION (RECOMPILE) end; exec FetchMembers_Recompile 'GH'; exec FetchMembers_Recompile 'NG';
Gbr. 7 Perilaku prosedur tersimpan dengan OPTION (RECOMPILE)
Kesimpulan
Dalam artikel ini, kita telah melihat bagaimana rencana eksekusi yang konsisten untuk prosedur tersimpan dapat menjadi masalah ketika data yang kita tangani miring. Kami juga telah mendemonstrasikan ini dalam praktik dan belajar tentang solusi umum untuk masalah tersebut. Saya berani mengatakan bahwa pengetahuan ini sangat berharga bagi pengembang yang menggunakan SQL Server. Ada sejumlah solusi lain untuk masalah ini – Brent Ozar masuk lebih dalam ke subjek dan menyoroti beberapa detail dan solusi yang lebih mendalam di SQLDay Poland 2017. Saya telah mencantumkan tautan yang sesuai di bagian referensi.
Referensi
Rencanakan Cache dan Pengoptimalan untuk Beban Kerja Adhoc
Mengidentifikasi dan Memperbaiki Masalah Pengendapan Parameter