Prosedur tersimpan vs inline SQL


27

Saya tahu prosedur tersimpan lebih efisien melalui jalur eksekusi (daripada sql inline dalam aplikasi). Namun, ketika ditekan, saya tidak tahu kenapa.

Saya ingin mengetahui alasan teknis untuk ini (dengan cara yang bisa saya jelaskan kepada seseorang nanti).

Adakah yang bisa membantu saya merumuskan jawaban yang bagus?


1
Sebuah permintaan parametrized benar adalah hanya sebagai baik sebagai prosedur yang tersimpan, dari sudut pandang kinerja. Keduanya dikompilasi sebelum digunakan pertama kali, keduanya akan menggunakan kembali rencana eksekusi yang di-cache pada eksekusi berikutnya, kedua rencana tersebut disimpan dalam cache paket yang sama dan keduanya akan ditangani dengan nama yang sama. Tidak ada manfaat kinerja untuk prosedur tersimpan lagi, di SQL Server hari ini.
marc_s

@marc_s itu benar jika kueri identik. Namun seperti yang saya tunjukkan dalam jawaban saya ada beberapa karakteristik kueri ad hoc yang dapat menjadi masalah kinerja bahkan untuk kueri yang tampak identik.
Aaron Bertrand

Jawaban:


42

Saya percaya sentimen ini benar pada satu titik, tetapi tidak dalam versi SQL Server saat ini. Seluruh masalah adalah bahwa di masa lalu pernyataan SQL ad hoc tidak dapat dioptimalkan dengan benar karena SQL Server hanya bisa mengoptimalkan / mengkompilasi pada tingkat batch. Sekarang kami memiliki optimasi tingkat pernyataan, sehingga kueri yang diparameterisasi dengan benar dari aplikasi dapat memanfaatkan rencana eksekusi yang sama dengan kueri yang tertanam dalam prosedur tersimpan.

Saya masih lebih suka prosedur tersimpan dari sisi DBA karena alasan berikut (dan beberapa dari mereka dapat memiliki dampak besar pada kinerja):

  • Jika saya memiliki beberapa aplikasi yang menggunakan kembali pertanyaan yang sama, prosedur yang tersimpan mengenkapsulasi logika itu, daripada mengotori kueri ad hoc yang sama beberapa kali dalam basis kode yang berbeda. Aplikasi yang menggunakan kembali kueri yang sama juga dapat mengalami penggembungan cache rencana, kecuali jika mereka disalin kata demi kata. Bahkan perbedaan dalam case dan white space dapat menyebabkan banyak versi dari paket yang sama disimpan (boros).
  • Saya dapat memeriksa dan memecahkan masalah apa yang dilakukan kueri tanpa memiliki akses ke kode sumber aplikasi atau menjalankan jejak mahal untuk melihat apa yang sedang dilakukan aplikasi.
  • Saya juga dapat mengontrol (dan tahu sebelumnya) kueri apa yang dapat dijalankan aplikasi, tabel apa yang dapat diakses dan dalam konteks apa, dll. Jika pengembang menulis kueri ad-hoc dalam aplikasi mereka, mereka harus datang menarik lengan baju saya setiap kali mereka membutuhkan akses ke meja yang saya tidak tahu atau tidak bisa memprediksi, atau jika saya kurang bertanggung jawab / antusias dan / atau sadar keamanan, saya hanya akan mempromosikan itu pengguna dbo sehingga mereka berhenti menggangguku. Biasanya ini dilakukan ketika pengembang melebihi jumlah DBA atau DBA yang keras kepala. Poin terakhir itu adalah masalah kami, dan kami harus lebih baik dalam memberikan pertanyaan yang Anda butuhkan.
  • Pada catatan terkait, satu set prosedur tersimpan adalah cara yang sangat mudah untuk menginventarisir secara tepat kueri apa yang mungkin berjalan di sistem saya. Segera setelah aplikasi diizinkan untuk memintas prosedur dan mengirimkan kueri ad-hoc sendiri, untuk menemukannya, saya harus menjalankan jejak yang mencakup seluruh siklus bisnis, atau menguraikan semua kode aplikasi (sekali lagi, bahwa Saya mungkin tidak memiliki akses ke) untuk menemukan sesuatu yang tampak seperti permintaan. Mampu melihat daftar prosedur tersimpan (dan menangkap satu sumber sys.sql_modules,, untuk referensi ke objek tertentu) membuat hidup semua orang lebih mudah.
  • Saya bisa berusaha lebih keras untuk mencegah injeksi SQL; bahkan jika saya mengambil input dan menjalankannya dengan SQL dinamis, saya dapat mengontrol banyak hal yang diperbolehkan terjadi. Saya tidak memiliki kendali atas apa yang dilakukan pengembang ketika membuat pernyataan SQL sebaris.
  • Saya dapat mengoptimalkan kueri (atau kueri) tanpa memiliki akses ke kode sumber aplikasi, kemampuan untuk membuat perubahan, pengetahuan tentang bahasa aplikasi untuk melakukannya secara efektif, otoritas (apalagi kerumitan) untuk mengkompilasi ulang dan menggunakan kembali aplikasi, dll. Ini sangat bermasalah jika aplikasi didistribusikan.
  • Saya dapat memaksa opsi set tertentu dalam prosedur tersimpan untuk menghindari permintaan individu agar tidak tunduk pada beberapa Lambat dalam aplikasi, cepat dalam SSMS? masalah. Berarti bahwa untuk dua aplikasi berbeda yang memanggil permintaan ad hoc, satu dapat memiliki SET ANSI_WARNINGS ON, dan yang lainnya dapat SET ANSI_WARNINGS OFF, dan mereka masing-masing akan memiliki salinan rencana mereka sendiri. Paket yang mereka peroleh tergantung pada parameter yang digunakan, statistik yang berlaku, dll. Saat pertama kali kueri dipanggil dalam setiap kasus, yang dapat mengarah ke paket yang berbeda dan karenanya kinerja yang sangat berbeda.
  • Saya dapat mengontrol hal-hal seperti tipe data dan bagaimana parameter digunakan, tidak seperti ORM tertentu - beberapa versi sebelumnya seperti EF akan parameterisasi kueri berdasarkan panjang parameter, jadi jika saya memiliki parameter N'Smith 'dan N lain' Johnson 'Saya akan mendapatkan dua versi rencana yang berbeda. Mereka sudah memperbaiki ini. Mereka sudah memperbaiki ini tetapi apa lagi yang masih rusak?
  • Saya dapat melakukan hal-hal yang belum dapat didukung oleh ORM dan kerangka kerja "perpustakaan" lainnya.

Karena itu, pertanyaan ini kemungkinan akan membangkitkan lebih banyak argumen keagamaan daripada debat teknis. Jika kita melihat itu terjadi, kita mungkin akan mematikannya.


2
Alasan lain untuk prosedur tersimpan? Untuk pertanyaan panjang dan rumit, Anda harus mendorong kueri ke server setiap kali, kecuali itu sproc, maka Anda pada dasarnya hanya mendorong "exec sprocname" dan beberapa parameter. Ini bisa membuat perbedaan pada jaringan yang lambat (atau sibuk).
David Crowell

0

Sementara saya menghormati pengirim, saya dengan rendah hati tidak setuju dengan jawaban yang diberikan dan bukan karena "alasan agama". Dengan kata lain, saya percaya tidak ada fasilitas yang disediakan Microsoft yang mengurangi kebutuhan akan panduan untuk menggunakan prosedur tersimpan.

Setiap panduan yang diberikan kepada pengembang yang mendukung penggunaan kueri teks SQL mentah harus diisi dengan banyak peringatan, sehingga saya pikir saran yang paling bijaksana adalah mendorong penggunaan Prosedur yang Disimpan dan, mencegah tim pengembang Anda untuk terlibat dalam praktik. menanamkan pernyataan SQL dalam kode, atau mengirimkan permintaan SQL mentah, berbasis teks biasa, di luar SQL SPROCs (prosedur tersimpan).

Saya pikir jawaban sederhana untuk pertanyaan mengapa menggunakan SPROC adalah sebagai submitter yang diperkirakan: SPROC diuraikan, dioptimalkan, dan dikompilasi. Dengan demikian, rencana kueri / eksekusi mereka di-cache karena Anda telah menyimpan representasi statis dari sebuah query dan Anda, biasanya, akan memvariasikannya hanya dengan parameter, yang tidak benar dalam kasus pernyataan SQL yang disalin / ditempelkan yang kemungkinan berubah dari halaman-ke-halaman-dan komponen / tingkat, dan sering divariasikan sejauh tabel yang berbeda, bahkan nama database, dapat ditentukan dari panggilan ke panggilan. Mengizinkan jenis dinamis ad hoc iniPengiriman SQL, sangat mengurangi kemungkinan Mesin DB untuk menggunakan kembali rencana permintaan untuk pernyataan ad hoc Anda, menurut beberapa aturan yang sangat ketat. Di sini, saya membuat perbedaan antara permintaan ad hoc dinamis (sesuai dengan pertanyaan yang diajukan) versus penggunaan Sistem SPROC sp_executesql yang efisien.

Lebih khusus lagi, ada komponen berikut:

  • Paket permintaan serial dan paralel yang tidak menampung konteks pengguna dan memungkinkan untuk digunakan kembali oleh mesin DB.
  • Konteks eksekusi yang memungkinkan penggunaan kembali rencana kueri oleh pengguna baru dengan parameter data berbeda.
  • Cache prosedur yang diminta oleh mesin DB untuk menciptakan efisiensi yang kami cari.

Ketika pernyataan SQL dikeluarkan dari halaman web, disebut "pernyataan ad hoc", mesin mencari rencana eksekusi yang ada untuk menangani permintaan tersebut. Karena ini adalah teks yang dikirimkan dari pengguna, itu akan dicerna, diuraikan, dikompilasi, dan dieksekusi, jika valid. Saat ini ia akan menerima biaya permintaan nol. Biaya permintaan digunakan ketika mesin DB menggunakan algoritme untuk menentukan rencana eksekusi mana yang akan diusir dari cache.

Kueri ad hoc menerima nilai biaya kueri asli nol, secara default. Setelah eksekusi selanjutnya dari teks kueri ad hoc yang sama persis, oleh proses pengguna lain (atau yang sama), biaya permintaan saat ini diatur ulang ke biaya kompilasi asli. Karena biaya kompilasi permintaan ad hoc kami adalah nol, ini bukan pertanda baik untuk kemungkinan penggunaan kembali. Jelas, nol adalah bilangan bulat yang paling tidak dihargai, tetapi mengapa itu harus digusur?

Ketika tekanan memori muncul, dan mereka akan melakukannya jika Anda memiliki situs yang sering digunakan, mesin DB menggunakan algoritma pembersihan untuk menentukan bagaimana ia dapat memperoleh kembali memori yang menggunakan cache Prosedur. Ia menggunakan biaya permintaan saat ini untuk memutuskan rencana mana yang akan digusur. Seperti yang Anda duga, paket dengan biaya nol adalah yang pertama diusir dari cache karena nol pada dasarnya berarti "tidak ada pengguna saat ini, atau referensi untuk, paket ini".

  • Catatan: Paket eksekusi ad hoc - Biaya saat ini dinaikkan oleh setiap proses pengguna, oleh biaya kompilasi asli paket. Namun, tidak ada biaya maksimum paket yang dapat melebihi biaya kompilasi aslinya ... dalam kasus permintaan ad hoc ... nol. Jadi, itu akan "ditingkatkan" dengan nilai itu ... nol - yang pada dasarnya berarti akan tetap menjadi rencana biaya terendah.

Oleh karena itu, sangat mungkin bahwa rencana seperti itu akan digusur terlebih dahulu ketika tekanan ingatan muncul.

Jadi, jika Anda memiliki server build-out dengan banyak memori "di luar kebutuhan Anda", Anda mungkin tidak mengalami masalah ini sesering server sibuk yang hanya memiliki "cukup" memori untuk menangani beban kerjanya. (Maaf, kapasitas dan pemanfaatan memori server agak subyektif / relatif, meskipun algoritme tidak.)

Sekarang, jika saya secara faktual salah tentang satu atau lebih poin, saya tentu terbuka untuk dikoreksi.

Terakhir, penulis menulis:

"Sekarang kami memiliki optimasi tingkat pernyataan, sehingga kueri yang diparameterisasi dengan benar dari aplikasi dapat memanfaatkan rencana eksekusi yang sama dengan kueri yang tertanam dalam prosedur tersimpan."

Saya yakin penulis mengacu pada opsi "optimalkan untuk beban kerja ad hoc".

Jika demikian, opsi ini memungkinkan untuk proses dua langkah yang menghindari segera mengirim paket permintaan penuh ke cache Prosedur. Itu hanya mengirim potongan permintaan yang lebih kecil di sana. Jika panggilan kueri yang tepat dikirim kembali ke server saat rintisan kueri masih dalam cache Prosedur, rencana eksekusi kueri lengkap disimpan ke cache Prosedur, pada saat itu. Ini menghemat memori, yang selama insiden tekanan memori, mungkin memungkinkan algoritma penggusuran untuk mengusir rintisan Anda lebih jarang daripada rencana permintaan yang lebih besar yang cache. Sekali lagi, ini tergantung pada memori dan pemanfaatan server Anda.

Namun, Anda harus mengaktifkan opsi ini, karena tidak aktif secara default.

Terakhir, saya ingin menekankan bahwa, seringkali, alasan utama pengembang menanamkan SQL di halaman, komponen, dan tempat-tempat lain, adalah karena mereka ingin menjadi fleksibel dan mengirimkan query SQL dinamis ke mesin database. Oleh karena itu, dalam Kasus Penggunaan dunia nyata, mengirimkan teks yang sama, panggilan-panggilan, tidak mungkin terjadi seperti halnya caching / efisiensi yang kami cari, ketika mengirimkan permintaan ad hoc ke SQL Server.

Untuk informasi tambahan, silakan lihat:

https://technet.microsoft.com/en-us/library/ms181055(v=sql.105).aspx
http://sqlmag.com/database-performance-tuning/don-t-fear-dynamic-sql

Terbaik,
Henry


4
Saya telah membaca beberapa paragraf posting Anda, dengan hati-hati, dua atau tiga kali, dan saya masih tidak tahu apa yang ingin Anda sampaikan. Dalam beberapa kasus Anda muncul di akhir kalimat untuk mengatakan kebalikan dari apa yang kalimat tersebut coba katakan. Anda benar-benar perlu mengoreksi dan mengedit kiriman ini dengan hati-hati.
Pieter Geerkens

Terima kasih atas umpan balik Pieter. Jika ini masalahnya, mungkin saya harus mempersingkat kalimat saya untuk memperjelas poin ini. Bisakah Anda memberikan contoh di mana saya tampaknya menyatakan kebalikan dari pemikiran asli? Sangat dihargai.
Henry

Tidak, saya tidak bermaksud Mengoptimalkan untuk Beban Kerja Ad Hoc, maksud saya adalah pengoptimalan tingkat pernyataan. Dalam SQL Server 2000, misalnya, prosedur tersimpan akan dikompilasi secara keseluruhan, jadi tidak ada cara bagi aplikasi untuk menggunakan kembali rencana untuk kueri ad hoc sendiri yang kebetulan cocok dengan sesuatu dalam prosedur. Saya akan mengatakan bahwa saya setuju dengan Pieter - banyak hal yang Anda katakan sulit untuk diikuti. Hal-hal seperti "Saya percaya tidak ada fasilitas yang disediakan Microsoft yang mengurangi kebutuhan akan panduan untuk menggunakan prosedur tersimpan." rumit dan membutuhkan terlalu banyak penguraian untuk dipahami. MENURUT OPINI SAYA.
Aaron Bertrand

1
sepertinya keengganan Anda untuk "ad hoc" sql didasarkan pada gagasan bahwa sql entah bagaimana berubah di antara eksekusi ... ini sepenuhnya tidak benar ketika parameterisasi terlibat.
b_levitt

0

TLDR: Tidak ada perbedaan kinerja yang cukup besar antara keduanya selama inline sql Anda parameter.

Inilah alasan mengapa saya secara bertahap menghapus prosedur tersimpan:

  • Kami menjalankan lingkungan aplikasi 'beta' - lingkungan yang paralel dengan produksi yang berbagi basis data produksi. Karena, kode db berada pada tingkat aplikasi dan perubahan struktur db jarang terjadi, kami dapat memungkinkan orang untuk mengkonfirmasi fungsionalitas baru di luar QA dan melakukan penyebaran di luar jendela penyebaran produksi tetapi masih menyediakan fungsionalitas produksi dan perbaikan tidak kritis. Ini tidak akan mungkin terjadi jika setengah dari kode aplikasi ada dalam DB.

  • Kami berlatih devops di tingkat basis data (gurita + dacpacs). Namun, sementara lapisan bisnis ke atas pada dasarnya dapat dibersihkan dan diganti dan pemulihan hanya sebaliknya, itu tidak benar untuk perubahan tambahan dan berpotensi merusak yang harus pergi ke database. Karenanya, kami memilih untuk menjaga penyebaran DB kami lebih ringan dan lebih jarang.

  • Untuk menghindari salinan yang persis sama dari kode yang sama untuk parameter opsional, kita sering akan menggunakan pola 'di mana @var adalah null atau @ var = table.field'. Dengan proc yang tersimpan, Anda kemungkinan akan mendapatkan rencana eksekusi yang sama, meskipun ada maksud yang agak berbeda, dan karenanya mengalami masalah kinerja atau menghilangkan rencana yang di-cache dengan petunjuk 'kompilasi ulang'. Namun, dengan sedikit kode sederhana yang menambahkan komentar "tanda tangan" di akhir sql, kita dapat memaksa rencana berbeda berdasarkan variabel mana yang nol (tidak dapat ditafsirkan sebagai rencana yang berbeda untuk semua kombinasi variabel - hanya nol vs bukan null).

  • Saya bisa membuat perubahan dramatis pada hasil dengan hanya perubahan kecil dengan cepat ke sql. Misalnya, saya dapat memiliki pernyataan yang ditutup dengan dua CTE, "Raw", dan "ReportReady". Tidak ada yang mengatakan kedua CTE harus digunakan. Pernyataan sql saya kemudian dapat:

    ...

    pilih * dari {(format)} "

Ini memungkinkan saya untuk menggunakan metode logika bisnis yang sama persis untuk panggilan api yang ramping dan laporan yang perlu lebih rinci memastikan bahwa saya tidak menduplikasi logika yang rumit.

  • ketika Anda memiliki aturan "procs saja", Anda berakhir dengan satu ton redundansi di sebagian besar sql Anda yang akhirnya menjadi CRUD - Anda mengikat semua parameter, Anda mendaftar semua parameter dalam tanda tangan proc, (dan sekarang Anda berada di file yang berbeda di proyek yang berbeda), Anda memetakan parameter sederhana itu ke kolom mereka. Ini menciptakan pengalaman pengembangan yang cukup terpisah.

Ada alasan yang sah untuk menggunakan procs:

  • Keamanan - Anda punya lapisan lain di sini yang harus dilalui aplikasi. Jika akun layanan aplikasi tidak diperbolehkan menyentuh tabel, tetapi hanya memiliki izin 'jalankan' pada procs, Anda memiliki perlindungan ekstra. Ini tidak membuatnya diberikan karena memang ada biaya, tetapi itu adalah kemungkinan.

  • Penggunaan Kembali - Walaupun saya akan mengatakan bahwa penggunaan kembali sebagian besar harus terjadi pada lapisan bisnis untuk memastikan Anda tidak melewati aturan bisnis yang terkait dengan non-db, kami masih memiliki jenis procs dan fungsi utilitas "digunakan di mana saja" tingkat rendah yang digunakan di mana-mana ".

Ada beberapa argumen yang tidak benar-benar mendukung procs atau IMO yang mudah dimitigasi:

  • Penggunaan Kembali - Saya menyebutkan ini di atas sebagai "plus", tetapi juga ingin menyebutkannya di sini bahwa penggunaan kembali sebagian besar harus terjadi pada lapisan bisnis. Proc untuk menyisipkan catatan tidak boleh dianggap "dapat digunakan kembali" ketika lapisan bisnis mungkin juga memeriksa layanan non-db lainnya.

  • Cache plan mengasapi - satu-satunya cara ini akan menjadi masalah adalah jika Anda menggabungkan nilai daripada parameterisasi. Fakta bahwa Anda jarang mendapatkan lebih dari satu paket per proc sebenarnya sering menyakiti Anda ketika Anda memiliki 'atau' dalam permintaan

  • Ukuran pernyataan - kb tambahan dari pernyataan sql atas nama proc biasanya akan diabaikan relatif terhadap data yang datang kembali. Jika tidak apa-apa bagi Entitas, tidak apa-apa bagi saya.

  • Melihat kueri yang tepat - Membuat kueri mudah ditemukan dalam kode semudah menambahkan lokasi panggilan sebagai komentar pada kode. Membuat kode dapat disalin dari kode c # ke ssms semudah beberapa interpolasi kreatif dan penggunaan komentar:

        //Usage /*{SSMSOnly_}*/Pure Sql To run in SSMS/*{_SSMSOnly}*/
        const string SSMSOnly_ = "*//*<SSMSOnly>/*";
        const string _SSMSOnly = "*/</SSMSOnly>";
        //Usage /*{NetOnly_}{InterpolationVariable}{_NetOnly}*/
        const string NetOnly_ = "*/";
        const string _NetOnly = "/*";
  • Sql Injection - Parameterkan pertanyaan Anda. Selesai Ini sebenarnya bisa dibatalkan jika proc malah menggunakan sql dinamis.

  • Bypassing deployment - Kami juga mempraktikkan devops di level database, jadi ini bukan pilihan bagi kami.

  • "Lambat dalam aplikasi, cepat dalam SSMS" - Ini adalah masalah caching paket yang mempengaruhi kedua belah pihak. Opsi yang disetel hanya menyebabkan rencana baru dikompilasi yang tampaknya memperbaiki masalah untuk variabel THE ONE SET OFF. Ini hanya menjawab mengapa Anda melihat hasil yang berbeda - opsi yang disetel sendiri TIDAK memperbaiki masalah parameter mengendus.

  • Paket eksekusi sql sebaris tidak di-cache - Cukup salah. Pernyataan parameter, seperti nama proc dengan cepat hash dan kemudian rencana dicari oleh hash itu. 100% sama.

  • Untuk lebih jelasnya saya berbicara tentang sql inline mentah bukan kode yang dihasilkan dari ORM - kami hanya menggunakan Dapper yang merupakan ORM mikro yang terbaik.

https://weblogs.asp.net/fbouma/38178

/programming//a/15277/852208

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.