Saya memiliki kasus khusus di mana parameter sniffing menyebabkan rencana eksekusi "buruk" mendarat di cache paket, menyebabkan eksekusi selanjutnya dari prosedur tersimpan saya menjadi sangat lambat. Saya dapat "memecahkan" masalah ini dengan variabel lokal OPTIMIZE FOR ... UNKNOWN
,, dan OPTION(RECOMPILE)
. Namun, saya juga bisa menyelami kueri dan mencoba mengoptimalkannya.
Saya mencoba menentukan apakah saya harus : diberi waktu terbatas untuk memperbaiki masalah, saya ingin tahu biaya tidak melakukannya. Seperti yang saya lihat, jika saya tetap bertahan OPTION(RECOMPILE)
, efek bersihnya adalah rencana kueri dibuat kembali setiap kali kueri dijalankan. Jadi, saya pikir saya perlu tahu:
Bagaimana cara mengetahui biaya pembuatan rencana kueri?
Untuk menjawab pertanyaan saya sendiri, saya telah mencari di Google (mis. Dengan pertanyaan ini ), dan saya telah membaca dokumentasi kolom untuk dm_exec_query_stats
DMV . Saya juga telah memeriksa jendela output dalam SSMS untuk "Rencana Kueri Aktual" untuk menemukan info ini. Akhirnya, saya sudah mencari DBA.SE . Tak satu pun dari mereka mengarah pada jawaban.
Adakah yang bisa memberitahuku? Apakah mungkin menemukan atau mengukur waktu yang diperlukan untuk pembuatan rencana?
sumber
Jawaban:
Anda bisa melihat properti dari simpul akar dalam rencana kueri, misalnya:
(tangkapan layar dari Sentry One Plan Explorer gratis )
Informasi ini juga tersedia dengan menanyakan cache rencana, misalnya menggunakan permintaan berdasarkan hubungan berikut:
Untuk perawatan lengkap dari opsi yang Anda miliki untuk menangani pertanyaan semacam ini, lihat artikel Erland Sommarskog yang baru - baru ini diperbarui .
sumber
Dengan asumsi "biaya" adalah dalam hal waktu (meskipun tidak yakin apa lagi yang bisa dalam hal ;-), maka paling tidak Anda harus bisa merasakannya dengan melakukan sesuatu seperti berikut:
Item pertama yang dilaporkan di tab "Pesan" adalah:
Saya akan menjalankan ini setidaknya 10 kali dan rata-rata milidetik "CPU" dan "Berlalu".
Idealnya Anda akan menjalankan ini di Produksi sehingga Anda bisa mendapatkan perkiraan waktu yang sebenarnya, tetapi jarang ada orang yang diizinkan untuk menghapus cache rencana di Produksi. Untungnya, mulai di SQL Server 2008 menjadi mungkin untuk menghapus paket tertentu dari cache. Dalam hal ini Anda dapat melakukan hal berikut:
Namun, tergantung pada variabilitas dari nilai-nilai yang diteruskan untuk parameter (s) menyebabkan rencana cache "buruk", ada metode lain untuk mempertimbangkan bahwa itu adalah jalan tengah antara
OPTION(RECOMPILE)
danOPTION(OPTIMIZE FOR UNKNOWN)
: Dynamic SQL. Ya, saya mengatakannya. Dan yang saya maksud adalah Dynamic SQL non-parameter. Inilah sebabnya.Anda jelas memiliki data yang memiliki distribusi tidak merata, setidaknya dalam hal satu atau lebih nilai parameter input. Kelemahan dari opsi yang disebutkan adalah:
OPTION(RECOMPILE)
akan menghasilkan rencana untuk setiap eksekusi dan Anda tidak akan pernah dapat mengambil manfaat dari penggunaan kembali paket apa pun , bahkan jika nilai parameter yang diteruskan lagi identik dengan proses sebelumnya. Untuk procs yang sering dipanggil - sekali setiap beberapa detik atau lebih sering - ini akan menyelamatkan Anda dari situasi yang mengerikan sesekali, tetapi masih membuat Anda dalam situasi yang selalu tidak terlalu hebat.OPTION(OPTIMIZE FOR (@Param = value))
akan menghasilkan rencana berdasarkan nilai tertentu, yang dapat membantu beberapa kasus tetapi tetap membuat Anda terbuka untuk masalah saat ini.OPTION(OPTIMIZE FOR UNKNOWN)
akan menghasilkan rencana berdasarkan pada jumlah apa untuk distribusi rata-rata, yang akan membantu beberapa pertanyaan tetapi merugikan yang lain. Ini harus sama dengan opsi menggunakan variabel lokal.Dynamic SQL, bagaimanapun, ketika dilakukan dengan benar , akan memungkinkan berbagai nilai yang diteruskan untuk memiliki rencana kueri terpisah mereka sendiri yang ideal (well, sebanyak yang akan terjadi). Biaya utama di sini adalah bahwa ketika variasi nilai yang diteruskan meningkat, jumlah rencana eksekusi dalam cache meningkat, dan mereka mengambil memori. Biaya kecil adalah:
perlu memvalidasi parameter string untuk mencegah SQL Suntikan
mungkin perlu mengatur Sertifikat dan Pengguna berbasis Sertifikat untuk mempertahankan abstraksi keamanan yang ideal karena Dynamic SQL memerlukan izin tabel langsung.
Jadi, inilah cara saya mengelola situasi ini ketika saya memiliki procs yang dipanggil lebih dari sekali per detik dan mencapai beberapa tabel, masing-masing dengan jutaan baris. Saya telah mencoba
OPTION(RECOMPILE)
tetapi ini terbukti terlalu merusak proses dalam 99% kasus yang tidak memiliki parameter sniffing / masalah rencana cache yang buruk. Dan harap diingat bahwa salah satu dari procs ini memiliki sekitar 15 pertanyaan di dalamnya dan hanya 3 - 5 dari mereka yang dikonversi ke Dynamic SQL seperti dijelaskan di sini; SQL dinamis tidak digunakan kecuali jika diperlukan untuk permintaan tertentu.Jika ada beberapa parameter input untuk prosedur tersimpan, cari tahu mana yang digunakan dengan kolom yang memiliki distribusi data sangat berbeda (dan karenanya menyebabkan masalah ini) dan mana yang digunakan dengan kolom yang memiliki distribusi lebih merata (dan tidak boleh menyebabkan masalah ini).
Membangun string SQL dinamis menggunakan parameter untuk par input input proc yang terkait dengan kolom yang terdistribusi secara merata. Parameterisasi ini membantu mengurangi peningkatan yang dihasilkan dalam rencana pelaksanaan di cache yang terkait dengan permintaan ini.
Untuk parameter lainnya yang terkait dengan distribusi yang sangat bervariasi, parameter tersebut harus digabungkan ke dalam SQL dinamis sebagai nilai literal. Karena kueri unik ditentukan oleh perubahan apa pun pada teks kueri, memiliki
WHERE StatusID = 1
kueri berbeda, dan karenanya, rencana kueri berbeda, daripada memilikiWHERE StatusID = 2
.Jika salah satu parameter input proc yang akan digabungkan ke dalam teks kueri adalah string, maka mereka harus divalidasi untuk melindungi terhadap SQL Injection (meskipun ini lebih kecil kemungkinannya terjadi jika string yang diteruskan dihasilkan oleh aplikasi dan bukan pengguna, tapi tetap saja). Setidaknya lakukan
REPLACE(@Param, '''', '''''')
untuk memastikan bahwa tanda kutip tunggal menjadi lolos tanda kutip tunggal.Jika perlu, buat Sertifikat yang akan digunakan untuk membuat Pengguna dan menandatangani prosedur tersimpan sehingga izin meja langsung akan diberikan hanya kepada Pengguna berbasis Sertifikat baru dan tidak untuk
[public]
atau kepada Pengguna yang seharusnya tidak memiliki izin seperti itu. .Contoh proc:
sumber
OPTION
atas permintaan saya), dan tidak akan menyakiti saya terlalu banyak karena sproc ini juga dimanfaatkan dalam tes integrasi. - Dalam hal apapun: terima kasih atas wawasan Anda!