Збережені процедури проти вбудованого SQL


27

Я знаю, що збережені процедури є більш ефективними через шлях виконання (ніж вбудований sql в додатках). Однак при натисканні я не надто добре знаю, чому саме.

Мені хотілося б знати технічні міркування для цього (таким чином, щоб я могла це пояснити комусь пізніше).

Хтось може допомогти мені сформулювати гарну відповідь?


1
Належним чином параметризрвані запити є настільки ж добре , як зберігається процедура, з точки зору продуктивності. Обидва збираються до першого використання, обидва будуть використовувати повторно кешований план виконання при наступних виконаннях, обидва плани зберігаються в одному кеш-плані одного плану, і обидва будуть оброблятися тим самим іменем. Сьогодні вже немає переваг продуктивності для збереженої процедури у SQL Server.
marc_s

@marc_s це правда, якщо запити однакові. Однак, як я зазначив у своїй відповіді, є деякі характеристики спеціальних запитів, які можуть бути проблемами ефективності навіть для запитів, які здаються однаковими.
Аарон Бертран

Відповіді:


42

Я вважаю, що такі настрої були істинними, але не в сучасних версіях SQL Server. Вся проблема полягала в тому, що за старих часів спеціальні заяви SQL не могли бути належним чином оптимізовані, оскільки SQL Server міг оптимізувати / компілювати лише на пакетному рівні. Тепер у нас є оптимізація рівня висловлювань, тож правильно параметризований запит, що надходить із програми, може скористатися тим самим планом виконання, що і цей запит, вбудований у збережену процедуру.

Я все ще віддаю перевагу збереженим процедурам з боку DBA з наступних причин (і декілька з них можуть мати величезний вплив на продуктивність):

  • Якщо у мене є кілька додатків, які повторно використовують одні й ті самі запити, збережена процедура інкапсулює цю логіку, а не засмічує один і той же спеціальний запит кілька разів у різних базах кодів. Програми, які повторно використовують ті самі запити, також можуть піддаватися плануванню розширення кешу, якщо вони не будуть скопійовані дослівно. Навіть відмінності між корпусом та пробілом можуть призвести до збереження кількох версій одного плану (марно).
  • Я можу перевірити та усунути неполадки, що робить запит, не маючи доступу до вихідного коду програми та не запускаючи дорогі сліди, щоб точно побачити, що робить програма.
  • Я також можу контролювати (і заздалегідь знати), які запити може запускати додаток, до яких таблиць він може отримати доступ та в якому контексті тощо. Якщо розробники пишуть запити спеціально у своїй програмі, їм або доведеться приходьте, перетягніть рукав сорочки кожного разу, коли їм потрібен доступ до столу, про який я не знав або не міг передбачити, або якщо я менш відповідальний / захоплений та / або усвідомлюючи безпеку, я просто збираюся просувати це користувачеві dbo, щоб вони перестали клопотати мене. Зазвичай це робиться, коли розробники перевершують кількість DBA або DBA вперті. Цей останній момент - наш поганий, і ми повинні бути кращими щодо надання запитів, які вам потрібні.
  • У відповідній примітці набір збережених процедур - це дуже простий спосіб інвентаризації того, які запити можуть працювати в моїй системі. Як тільки програмі дозволено обходити процедури та надсилати власні спеціальні запити, для того, щоб їх знайти, я повинен провести слід, який охоплює весь бізнес-цикл, або проаналізувати весь код програми (знову ж таки, що Я не можу отримати доступ до), щоб знайти щось, що схоже на запит. Можливість перегляду списку збережених процедур (і прив'язування до одного джерела sys.sql_modules, для посилань на конкретні об'єкти) значно полегшує життя кожного.
  • Я можу піти набагато більше, щоб запобігти ін'єкції SQL; навіть якщо я беру введення та виконую його за допомогою динамічного SQL, я можу багато контролювати те, що дозволено. У мене немає контролю над тим, що робить розробник під час створення вбудованих операторів SQL.
  • Я можу оптимізувати запит (або запити), не маючи доступу до вихідного коду програми, можливості вносити зміни, знання мови програми, щоб зробити це ефективно, повноваження (не маю на увазі клопоту) перекомпілювати та повторно розгорнути додаток тощо. Це особливо проблематично, якщо програма розповсюджується.
  • Я можу змусити певні параметри набору в межах збереженої процедури, щоб уникнути того, щоб окремі запити піддавались деяким повільним у програмі, швидким у SSMS? проблеми. Це означає, що для двох різних додатків, що викликають спеціальний запит, один може мати SET ANSI_WARNINGS ON, а другий може мати SET ANSI_WARNINGS OFF, і кожен з них має свою копію плану. План, який вони отримують, залежить від параметрів, які використовуються, статистики на місці тощо. Перший раз, коли запит викликається у кожному конкретному випадку, це може призвести до різних планів і, отже, до дуже різної продуктивності.
  • Я можу контролювати такі речі, як типи даних та те, як використовуються параметри, на відміну від певних ORM - деякі попередні версії таких речей, як EF, параметризували б запит на основі довжини параметра, тож якби у мене був параметр N'Smith 'та інший N' Джонсон 'Я отримав би дві різні версії плану. Вони це виправили. Вони це виправили, але що ще порушено?
  • Я можу робити те, що ORM та інші "корисні" рамки та бібліотеки ще не в змозі підтримати.

Однак, це питання може викликати більше релігійних аргументів, ніж технічні дискусії. Якщо ми побачимо, що це відбувається, ми, ймовірно, його закриємо.


2
Ще одна причина збережених процедур? Для довгих складних запитів вам потрібно кожного разу підштовхувати запит до сервера, якщо тільки це не проросток, то ви, як правило, просто натискаєте "exec sprocname" та кілька параметрів. Це може змінити ситуацію в повільній (або зайнятій) мережі.
Девід Кроуелл

0

Хоча я поважаю представника, я смиренно не згоден з наданою відповіддю, а не з "релігійних причин". Іншими словами, я вважаю, що Microsoft не створила жодної можливості, яка б зменшила потребу в керівництві щодо використання збережених процедур.

Будь-які вказівки, що надаються розробнику, що сприяє використанню необроблених текстових запитів SQL, повинні бути заповнені багатьма застереженнями, так що, на мою думку, найдоцільнішою порадою є велике заохочення до використання збережених процедур та відмовляти ваші команди розробників від участі в практиці вбудовування операторів SQL в код або подання необроблених, звичайних текстових запитів на основі SQL за межами SQL SPROC (збережені процедури).

Я думаю, що відповідь на питання про те, навіщо використовувати SPROC, є таким, як подав заявник: SPROC розбираються, оптимізуються та компілюються. Таким чином, плани їх запиту / виконання кешуються, тому що ви зберегли статичне подання запиту, і ви, як правило, будете змінювати його лише за параметрами, що не відповідає дійсності у випадку скопійованих / вставлених SQL-операторів, які ймовірно перетворюються від сторінки до сторінки та компонента / ярусу, і вони часто варіабелізовані настільки, що різні таблиці, навіть імена бази даних, можуть бути визначені від дзвінка до дзвінка. Дозволено для цього типу динамічних спеціальних заходівПредставлення SQL значно знижує ймовірність того, що Двигун БД повторно використовувати план запитів для ваших спеціальних висловлювань відповідно до деяких дуже суворих правил. Тут я розрізняю динамічні спеціальні запити (в дусі порушеного питання) проти використання ефективної системи SPROC sp_executesql.

Більш конкретно, є такі компоненти:

  • Послідовні та паралельні плани запитів, які не містять контексту користувача та дозволяють повторно використовувати двигуном БД.
  • Контекст виконання, який дозволяє повторно використовувати план запитів новим користувачем з різними параметрами даних.
  • Кеш процедури, який запитує двигун БД, щоб створити працездатність.

Коли оператор SQL видається з веб-сторінки, який називається "ad hoc заявою", двигун шукає існуючий план виконання для обробки запиту. Оскільки це текст, представлений користувачем, він буде прийнятий, проаналізований, складений та виконаний, якщо він дійсний. Цього разу він отримає нульову вартість запиту. Вартість запиту використовується, коли двигун БД використовує свій алгоритм, щоб визначити, які плани планується вилучити з кешу.

За замовчуванням спеціальні запити отримують початкове значення вартості запиту, яке дорівнює нулю. Після подальшого виконання такого ж тексту спеціального запиту іншим користувацьким процесом (або тим самим) поточна вартість запиту скидається до початкової вартості компіляції. Оскільки вартість компіляції спеціального запиту дорівнює нулю, це не означає, що можливість повторного використання. Очевидно, що нуль - це найменше ціле число, але навіщо його виселяти?

Коли тиски в пам'яті виникають і вони будуть, якщо у вас часто використовується сайт, двигун БД використовує алгоритм очищення, щоб визначити, як він може відновити пам'ять, яку використовує кеш процедури. Він використовує поточну вартість запиту, щоб вирішити, які плани виселити. Як ви здогадуєтесь, плани із вартістю нуля першими вилучаються з кешу, оскільки нуль по суті означає "немає поточних користувачів або посилань на цей план".

  • Примітка: Спеціальні плани виконання - Поточна вартість збільшується з кожним користувальницьким процесом на початкову вартість складання плану. Однак максимальна вартість плану не може бути більшою, ніж його початкова вартість компіляції ... у випадку спеціальних запитів ... нуль. Отже, він буде "збільшений" на цю величину ... нуль - що по суті означає, що він залишиться планом з найнижчою вартістю.

Тому цілком ймовірно, що такий план спочатку буде усунутий, коли виникне тиск в пам'яті.

Отже, якщо у вас є розробка сервера з великою кількістю пам’яті «поза вашими потребами», ви можете не відчувати цю проблему так часто, як зайнятий сервер, який має лише «достатню» пам’ять для управління навантаженням. (Вибачте, ємність та використання пам'яті сервера дещо суб'єктивні / відносні, хоча алгоритм цього немає.)

Тепер, якщо я фактично помиляюся щодо одного або декількох моментів, я, безумовно, готовий до виправлення.

Нарешті, автор написав:

"Зараз у нас є оптимізація рівня висловлювань, тож правильно параметризований запит, що надходить із програми, може скористатися тим самим планом виконання, що і цей запит, вбудований у збережену процедуру."

Я вважаю, що автор посилається на опцію "оптимізувати для спеціальних навантажень".

Якщо це так, ця опція дозволяє здійснити двоетапний процес, що дозволяє уникнути негайного надсилання повного плану запитів у кеш процедури. Він надсилає туди лише меншу заглушку запиту. Якщо точний виклик запиту буде відправлений назад на сервер, поки заглушка запиту все ще знаходиться в кеш-процедурі, то повний план виконання запиту зберігається в кеш-процедурі. Це економить на пам'яті, яка під час інцидентів тиску в пам'яті може дозволяти алгоритму виселення виселяти вашу заглушку рідше, ніж більш великий план запитів, який був кешований. Знову ж таки, це залежить від пам’яті та використання вашого сервера.

Однак цю опцію потрібно ввімкнути, оскільки вона за замовчуванням відключена.

Нарешті, я хочу наголосити, що часто саме причина розробників вбудовує SQL у сторінки, компоненти та інші місця - це тому, що вони хочуть бути гнучкими та надсилати динамічний запит SQL до двигуна бази даних. Отже, у реальному випадку використання, подання того самого тексту, переклик над дзвінками, навряд чи відбудеться, як і кешування / ефективність, до якої ми прагнемо, під час подання спеціальних запитів на SQL Server.

Для отримання додаткової інформації дивіться:

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

Найкраще,
Генрі


4
Я прочитав кілька абзаців вашої публікації, обережно, два-три рази, і досі не маю уявлення, які думки ви намагаєтеся передати. У деяких випадках вам здається, що в кінці речень ви говорите прямо протилежне тому, що почалося в спробі сказати. Вам справді потрібно уважно прочитати та відредагувати це представлення.
Пітер Геркенс

Дякую за відгук Пітер. Якщо це так, можливо, я повинен скоротити свої речення, щоб зробити ясніше зрозумілим. Чи можете ви надати приклад того, де я, мабуть, констатую протилежну від початкової думки? Цінується.
Генрі

Ні, я не мав на увазі Оптимізація для спеціальних робочих навантажень, я мав на увазі оптимізацію рівня заяв. Наприклад, у SQL Server 2000, збережена процедура буде зібрана в цілому, тому програма не змогла повторно використовувати план для власного спеціального запиту, який трапився, щоб відповідати чомусь процедурі. Я скажу, що я згоден з Пітером - за багатьма речами, які ви говорите, важко прослідкувати. Такі речі, як "Я вважаю, що Microsoft не надав можливості, що зменшує потребу в керівництві щодо використання збережених процедур". є зайвими складними і вимагають занадто сильного розбору, щоб зрозуміти. ІМХО.
Аарон Бертран

1
здається, що ваша неприязнь до "ad hoc" sql заснована на ідеї, що sql якимось чином змінюється між виконаннями ... це абсолютно не відповідає дійсності, коли йдеться про параметризацію.
b_levitt

0

TLDR: Існує не помітна різниця в продуктивності між цими двома, доки ваш вбудований sql параметризований.

Ось чому я повільно припиняю зберігати процедури:

  • Ми запускаємо "бета" прикладне середовище - середовище, паралельне виробництву, яке розділяє виробничу базу даних. Оскільки код db знаходиться на рівні програми, і зміни структури DB є рідкісними, ми можемо дозволити людям підтверджувати нові функціональні можливості за межами QA та виконувати розгортання поза вікном виробничого розгортання, але все ж надавати функціональність виробництва та некритичні виправлення. Це було б неможливо, якби половина коду програми була в БД.

  • Ми практикуємо депеш на рівні бази даних (восьминога + дакпаки). Однак, хоча бізнес-рівень і вище можна в основному очистити і замінити, а відновити саме зворотне, це не вірно для поступових і потенційно руйнівних змін, які повинні йти до баз даних. Отже, ми вважаємо за краще, щоб наші розгортання БД були легшими та рідшими.

  • Щоб уникнути майже точних копій одного і того ж коду для необов'язкових параметрів, ми часто будемо використовувати шаблон ", де @var - нуль або @ var = table.field". З накопичуваною програмою ви, ймовірно, отримаєте один і той же план виконання, незважаючи на досить різні наміри, і, таким чином, або виникнете проблеми з продуктивністю, або усунете кешовані плани з підказками 'перекомпілювати'. Однак, за допомогою простого бітового коду, який додає коментар "підпису" до кінця sql, ми можемо змусити різні плани, засновані на тому, що змінні були недійсними (не трактуватись як інший план для всіх змінних комбінацій - тільки null vs не нульовий).

  • Я можу внести кардинальні зміни в результати, лише з незначними змінами на ходу до sql. Наприклад, у мене може бути твердження, яке закривається двома CTE, "Raw" та "ReportReady". Ніщо не говорить про те, що обидва CTE повинні бути використані. Моє оператор sql може бути:

    ...

    виберіть * з {(формат)} "

Це дозволяє мені використовувати такий самий метод бізнес-логіки як для впорядкованого виклику api, так і для звіту, який повинен бути більш детальним, щоб не дублювати складну логіку.

  • коли у вас є правило "тільки для програми", ви отримуєте тону надмірності в переважній більшості вашого sql, що в кінцевому підсумку є CRUD - Ви прив'язуєте всі параметри, ви перераховуєте всі ці параметри в підписі proc, (і тепер ви знаходитесь в іншому файлі іншого проекту), ви позначаєте ці прості параметри в їх стовпцях. Це створює досить непересічний досвід розвитку.

Існує поважна причина використання програм:

  • Безпека. У вас тут ще один шар, що додаток повинно працювати через. Якщо в обліковому записі служби додатків заборонено торкатися таблиць, а лише мати дозвіл на 'виконання' у програмах, у вас є додатковий захист. Це не робить його даним, оскільки у нього є вартість, але це можливість.

  • Повторне використання - Хоча я б сказав, що повторне використання повинно відбуватися в основному на рівні бізнесу, щоб переконатися, що ви не обходите правила, пов’язані з непідвладними комерційними технологіями, у нас все ж є професійний, низькорівневий тип «використовуваних скрізь» утилітних програм та функцій.

Є деякі аргументи, які насправді не підтримують документи або легко пом'якшуються IMO:

  • Повторне використання - я згадував про це вище як «плюс», але також хотів тут зазначити, що повторне використання має значною мірою відбуватися на рівні бізнесу. Процедура для вставки запису не повинна вважатися багаторазовою, коли бізнес-рівень також може перевіряти інші послуги, що не мають db.

  • Розгортання плану кеш-пам'яті - єдиний спосіб, коли це буде проблемою, це якщо ви об'єднуєте значення, а не параметризуєте. Той факт, що ви рідко отримуєте більше одного плану на одну процедуру, насправді часто шкодить вам, коли у вас є запит "чи"

  • Розмір оператора - додаткові кілограми висловлювань sql над іменем proc, як правило, будуть незначними щодо даних, що повертаються. Якщо для Entities це нормально, для мене це нормально.

  • Бачити точний запит - спрощення пошуку запитів у коді так само просто, як додавання місця виклику як коментаря до коду. Зробити код, який можна скопіювати з c # коду до ssms, настільки ж просто, як і творча інтерполяція та використання коментарів:

        //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 - параметризуйте запити. Зроблено. Це насправді може бути скасовано, якщо замість процесора використовується динамічний sql.

  • Обхід розгортання - ми практикуємо депеш також на рівні бази даних, тому для нас це не варіант.

  • "Повільний у програмі, швидкий у SSMS" - Це проблема кешування плану, яка зачіпає обидві сторони. Параметри набору просто викликають складання нового плану, який, як видається, вирішує проблему для змінних ONE ONE OFF. Це відповідає лише тому, чому ви бачите різні результати - самі встановлені параметри НЕ вирішують проблему нюху параметрів.

  • Плани виконання вбудованого sql не кешовані - Просто помилково. Параметризований оператор, як і ім’я proc, швидко хеширується, і тоді цей хеш шукає план. Це 100% те саме.

  • Щоб було зрозуміло, я говорю про необроблений вбудований sql, який не генерується кодом від ORM - ми використовуємо лише Dapper, який у кращому випадку є мікро ORM

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

/programming//a/15277/852208

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.