Я знаю, что хранимые процедуры более эффективны через путь выполнения (чем встроенный SQL в приложениях). Тем не менее, когда нажата, я не супер знаю, почему.
Я хотел бы знать технические причины этого (таким образом, чтобы я мог объяснить это кому-то позже).
Может ли кто-нибудь помочь мне сформулировать хороший ответ?
Ответы:
Я считаю, что это чувство было правдой в какой-то момент, но не в текущих версиях SQL Server. Вся проблема заключалась в том, что в прежние времена специальные операторы SQL не могли быть должным образом оптимизированы, поскольку SQL Server мог оптимизировать / компилировать только на уровне пакета. Теперь у нас есть оптимизация на уровне операторов, поэтому правильно параметризованный запрос, поступающий из приложения, может использовать тот же план выполнения, что и этот запрос, встроенный в хранимую процедуру.
Я по-прежнему предпочитаю хранимые процедуры со стороны администратора баз данных по следующим причинам (и некоторые из них могут оказать огромное влияние на производительность):
sys.sql_modules
для ссылок на конкретные объекты) значительно облегчает жизнь всем.SET ANSI_WARNINGS ON
, а другое может иметьSET ANSI_WARNINGS OFF
, и у каждого из них будет своя копия плана. План, который они получают, зависит от используемых параметров, статистики и т. Д. При первом вызове запроса в каждом случае, что может привести к различным планам и, следовательно, к разной производительности.Тем не менее, этот вопрос может вызвать больше религиозных споров, чем технических дебатов. Если мы увидим это, мы, вероятно, закроем его.
источник
Хотя я уважаю заявителя, я смиренно не согласен с предоставленным ответом, а не по «религиозным причинам». Другими словами, я считаю, что Microsoft не предоставила средства, которое уменьшило бы потребность в руководстве по использованию хранимых процедур.
Любое руководство, предоставленное разработчику, которое поддерживает использование необработанных текстовых SQL-запросов, должно быть заполнено множеством предостережений, так что я думаю, что самый разумный совет - сильно поощрять использование хранимых процедур и отговаривать группы разработчиков от участия в практике. встраивания операторов SQL в код или отправки необработанных простых текстовых SQL-запросов вне SQL SPROC (хранимых процедур).
Я думаю, что простой ответ на вопрос о том, почему использовать SPROC, заключается в том, что создатель предположил: SPROC анализируются, оптимизируются и компилируются. Таким образом, их планы запросов / выполнения кэшируются, потому что вы сохранили статическое представление запроса, и вы, как правило, будете изменять его только по параметрам, что неверно в случае скопированных / вставленных операторов SQL, которые, вероятно, изменяются от страницы к странице и компоненту / уровню, и часто меняются в зависимости от того, какие разные таблицы, даже имена баз данных, могут быть указаны при вызове. Разрешение для этого типа динамического ad hocОтправка SQL значительно снижает вероятность того, что DB Engine повторно использует план запросов для ваших специальных операторов в соответствии с некоторыми очень строгими правилами. Здесь я делаю различие между динамическими специальными запросами (в духе поставленного вопроса) и использованием эффективной системы SPROC sp_executesql.
Более конкретно, существуют следующие компоненты:
Когда с веб-страницы выдается оператор SQL, называемый «оператор ad hoc», механизм ищет существующий план выполнения для обработки запроса. Поскольку это текст, отправленный пользователем, он будет принят, проанализирован, скомпилирован и выполнен, если он действителен. В это время он получит нулевую стоимость запроса. Стоимость запроса используется, когда механизм БД использует свой алгоритм, чтобы определить, какие планы выполнения следует исключить из кэша.
По умолчанию специальные запросы получают исходную стоимость запроса равную нулю. При последующем выполнении точно такого же специального текста запроса другим пользовательским процессом (или тем же самым) текущая стоимость запроса сбрасывается до первоначальной стоимости компиляции. Поскольку стоимость компиляции нашего специального запроса равна нулю, это не сулит ничего хорошего для возможности повторного использования. Очевидно, что ноль - это наименее значное целое число, но зачем его выселять?
Когда возникают проблемы с памятью, и они будут возникать, если у вас есть часто используемый сайт, механизм БД использует алгоритм очистки, чтобы определить, как он может восстановить память, которую использует кэш процедур. Он использует текущую стоимость запроса, чтобы решить, какие планы выселить. Как вы можете догадаться, планы с нулевой стоимостью являются первыми, которые будут выселены из кэша, потому что ноль, по сути, означает «нет текущих пользователей или ссылок на этот план».
Поэтому вполне вероятно, что такой план будет выселен первым, когда возникнет давление памяти.
Таким образом, если у вас есть серверная сборка с большим объемом памяти «вне ваших потребностей», вы можете столкнуться с этой проблемой не так часто, как с загруженным сервером, который имеет только «достаточную» память для обработки своей рабочей нагрузки. (Извините, объем памяти сервера и его использование несколько субъективны / относительны, хотя алгоритм не так.)
Теперь, если я на самом деле ошибаюсь по поводу одного или нескольких пунктов, я, безусловно, открыт для исправления.
Наконец, автор написал:
«Теперь у нас есть оптимизация на уровне операторов, поэтому правильно параметризованный запрос, поступающий из приложения, может использовать тот же план выполнения, что и этот запрос, встроенный в хранимую процедуру».
Я полагаю, что автор ссылается на опцию «оптимизировать для специальных рабочих нагрузок».
Если это так, эта опция позволяет выполнить двухэтапный процесс, который позволяет избежать немедленной отправки полного плана запроса в кэш процедур. Он отправляет туда только заглушку меньшего размера. Если точный вызов запроса отправляется обратно на сервер, в то время как заглушка запроса все еще находится в кэше процедур, полный план выполнения запросов в это время сохраняется в кэше процедур. Это экономит память, что во время инцидентов с памятью может позволить алгоритму вытеснения выселять вашу заглушку реже, чем больший план запроса, который был кэширован. Опять же, это зависит от вашей памяти сервера и использования.
Однако вы должны включить эту опцию, так как по умолчанию она отключена.
Наконец, я хочу подчеркнуть, что зачастую разработчики встраивают SQL в страницы, компоненты и другие места по той причине, что они хотят быть гибкими и отправлять динамический запрос SQL в ядро базы данных. Следовательно, в реальном случае использования отправка того же текста call-over-call вряд ли произойдет, как и эффективность кэширования, которую мы ищем при отправке специальных запросов на 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
Бест,
Генри
источник
TLDR: между ними нет заметной разницы в производительности, пока ваш встроенный sql параметризован.
Вот почему я постепенно отказался от хранимых процедур:
Мы запускаем «бета» среду приложения - среду, параллельную производственной, которая совместно использует производственную базу данных. Поскольку код БД находится на уровне приложения, а изменения структуры БД встречаются редко, мы можем позволить людям подтверждать новые функциональные возможности, выходящие за рамки контроля качества, и выполнять развертывания за пределами рабочего окна развертывания, но при этом предоставлять производственную функциональность и некритические исправления. Это было бы невозможно, если бы половина кода приложения была в БД.
Мы практикуем devops на уровне базы данных (осьминог + dacpacs). Тем не менее, хотя бизнес-уровень и его уровень в основном можно очистить и заменить, а восстановление - наоборот, это не относится к постепенным и потенциально разрушительным изменениям, которые должны быть внесены в базы данных. Следовательно, мы предпочитаем, чтобы развертывание наших БД было более легким и менее частым.
Чтобы избежать точных копий одного и того же кода для необязательных параметров, мы часто будем использовать шаблон 'где @var равен нулю или @ var = table.field'. С сохраненным процессом вы, вероятно, получите тот же план выполнения, несмотря на довольно разные намерения, и, таким образом, либо столкнетесь с проблемами производительности, либо откажетесь от кэшированных планов с подсказками «перекомпиляции». Тем не менее, с помощью небольшого фрагмента кода, который добавляет комментарий «подписи» в конец sql, мы можем форсировать разные планы, в зависимости от того, какие переменные были нулевыми (не следует интерпретировать как разные планы для всех комбинаций переменных - только ноль против не ноль).
Я могу внести кардинальные изменения в результаты с незначительными изменениями на лету на SQL. Например, у меня может быть оператор, который завершается двумя CTE: «Raw» и «ReportReady». Нет ничего, что говорит, что оба CTE должны быть использованы. Мой SQL-оператор может быть:
...
выберите * из {(формат)} "
Это позволяет мне использовать один и тот же метод бизнес-логики как для упрощенного вызова API, так и для отчета, который должен быть более подробным, чтобы избежать дублирования сложной логики.
Существуют веские причины для использования процедур:
Безопасность - у вас есть еще один слой, через который должно пройти приложение. Если учетная запись службы приложений не имеет права прикасаться к таблицам, а имеет только разрешение «выполнить» на процессах, вы получаете дополнительную защиту. Это не делает его само собой разумеющимся, поскольку оно имеет стоимость, но это возможно.
Повторное использование - хотя я бы сказал, что повторное использование должно в основном происходить на бизнес-уровне, чтобы убедиться, что вы не обходите бизнес-правила, не относящиеся к db, у нас все еще есть случайные, низкоуровневые типы утилит и функций.
Есть некоторые аргументы, которые на самом деле не поддерживают проки или легко смягчаются IMO:
Повторное использование - я упомянул это выше как «плюс», но также хотел упомянуть здесь, что повторное использование должно в основном происходить на уровне бизнеса. Процедура вставки записи не должна рассматриваться как «повторно используемая», если бизнес-уровень также может проверять другие службы, не относящиеся к БД.
Переполнение плана кэширования - единственная причина, по которой это будет проблемой, - это если вы объединяете значения, а не параметризуетесь. Тот факт, что вы редко получаете более одного плана на процесс, часто причиняет вам боль, когда в запросе есть «или»
Размер оператора - дополнительные килобайты SQL-операторов над именем процесса обычно незначительны по отношению к возвращаемым данным. Если это нормально для сущностей, это нормально для меня.
Просмотр точного запроса - облегчить поиск запросов в коде так же просто, как добавить местоположение вызывающего абонента в качестве комментария к коду. Сделать код копируемым из кода c # в ssms так же просто, как творческая интерполяция и использование комментариев:
Sql Injection - Параметризация ваших запросов. Выполнено. Это на самом деле может быть отменено, если вместо этого используется динамический sql.
В обход развертывания - мы также практикуем devops на уровне базы данных, поэтому это не вариант для нас.
«Медленно в приложении, быстро в SSMS» - это проблема кэширования плана, которая затрагивает обе стороны. Заданные параметры просто приводят к компиляции нового плана, который, по-видимому, решает проблему с переменными THE ONE SET OFF. Это только объясняет, почему вы видите разные результаты - сами установленные параметры НЕ решают проблему анализа параметров.
Планы выполнения встроенного SQL не кэшируются - просто ложь. Параметризованный оператор, точно так же, как имя процесса быстро хэшируется, а затем этот план ищет план. Это на 100% то же самое.
Чтобы было ясно, я говорю о сыром встроенном sql не сгенерированном коде из ORM - мы используем только Dapper, который в лучшем случае является микро ORM.
https://weblogs.asp.net/fbouma/38178
/programming//a/15277/852208
источник