Анализ параметров (или подмена) в SQL Server

Некоторое время назад у меня был запрос, который я довольно часто выполнял для одного из моих пользователей. Он все еще развивался и настраивался, но в конечном итоге он стабилизировался и работал довольно быстро, поэтому мы создали из него хранимую процедуру.

Пока все нормально.

Однако хранимая процедура была медленной. Никакой существенной разницы между запросом и процедурой, но изменение скорости было огромным.

[Фон, мы работаем с SQL Server 2005.]

Дружелюбный местный администратор базы данных (который здесь больше не работает) взглянул на хранимую процедуру и сказал: «Подмена параметров!» (Изменить: хотя кажется, что это, возможно, также известно как «анализ параметров», что может объяснить малое количество обращений в Google, когда я пытался его найти.)

Мы абстрагировали часть хранимой процедуры во вторую, обернули вызов этой новой внутренней процедуры в уже существующую внешнюю, называемую внешней, и, привет, это было так же быстро, как и исходный запрос.

Итак, что дает? Может кто-нибудь объяснить подмену параметров?

Бонусный кредит на

  • подчеркивая, как этого избежать
  • предлагая, как распознать возможную причину
  • обсудить альтернативные стратегии, например статистика, индексы, ключи, для смягчения ситуации

person Unsliced    schedule 17.10.2008    source источник
comment
Это не просто возможность, это определенность - спуфинга параметров не существует. Это анализ параметров.   -  person ErikE    schedule 06.02.2012


Ответы (8)


К вашему сведению - вам нужно знать кое-что еще, когда вы работаете с SQL 2005 и храните процессы с параметрами.

SQL Server скомпилирует план выполнения хранимой процедуры с первым используемым параметром. Итак, если вы запустите это:

usp_QueryMyDataByState 'Rhode Island'

План выполнения лучше всего работает с данными небольшого штата. Но если кто-то обернется и убежит:

usp_QueryMyDataByState 'Texas'

План выполнения, разработанный для данных размера Род-Айленд, может быть не столь эффективным для данных размера Техаса. Это может привести к неожиданным результатам при перезапуске сервера, потому что вновь сгенерированный план выполнения будет нацелен на любой параметр, который используется первым, не обязательно лучший. План не будет перекомпилирован до тех пор, пока для этого не появится серьезная причина, например, если будет перестроена статистика.

Вот тут-то и появляются планы запросов, и SQL Server 2008 предлагает множество новых функций, которые помогают администраторам баз данных закрепить конкретный план запроса в долгосрочной перспективе, независимо от того, какие параметры вызываются в первую очередь.

Меня беспокоит то, что когда вы перестраивали сохраненную процедуру, вы заставляли план выполнения перекомпилировать. Вы вызвали его с вашим любимым параметром, и тогда, конечно, это было быстро, но проблема могла быть не в сохраненной процедуре. Возможно, сохраненная процедура была перекомпилирована в какой-то момент с необычным набором параметров и, следовательно, с неэффективным планом запроса. Возможно, вы ничего не исправили, и вы можете столкнуться с той же проблемой при следующем перезапуске сервера или перекомпиляции плана запроса.

person Brent Ozar    schedule 19.10.2008
comment
Возьмите превосходную книгу Гранта Фритчи «SQL Server 2008 Query Performance Distilled», в которой он описывает все возможные варианты. Несмотря на то, что там написано «2008 год», он отлично подходит и для 2005 года. - person Brent Ozar; 12.11.2010
comment
Я рекомендую поискать два возможных решения: первое, которое я пробовал и которое работало, второе, как я подозреваю, сработает, но не пробовал на практике. - person John Ingle; 19.01.2012
comment
Во-первых, вы можете объявить локальные переменные для каждого параметра, скопировать параметры в эти переменные и использовать эти переменные в своем запросе, а не использовать параметры напрямую. Я не знаю, почему это работает и пахнет, но это решает проблему (с использованием Sql Server 2008). Во-вторых, посмотрите подсказку запроса OPTIMIZE FOR UNKNOWN. Вы можете прочитать об этом и другие советы на MSDN: msdn.microsoft.com/en -us / library / ms181714.aspx - person John Ingle; 19.01.2012
comment
Вот ссылка, по которой другие могут увидеть книгу, о которой говорил @brent-ozar - теперь на 2014 год smile.amazon.com/SQL-Server-Query-Performance-Tuning/dp/ * Версия * = 1 & * записей * = 0 - person Thronk; 03.09.2014

Да, я думаю, вы имеете в виду анализ параметров, который используется оптимизатором SQL Server для определения значений / диапазонов параметров, чтобы он мог выбрать лучший план выполнения для вашего запроса. В некоторых случаях SQL Server плохо справляется с анализом параметров и не выбирает лучший план выполнения для запроса.

Я считаю, что эта статья в блоге http://blogs.msdn.com/queryoptteam/archive/2006/03/31/565991.aspx имеет хорошее объяснение.

Кажется, что администратор баз данных в вашем примере выбрал вариант № 4, чтобы переместить запрос в другой sproc в отдельный процедурный контекст.

Вы также могли использовать с перекомпиляцией в исходном sproc или использовать параметр optimize for для параметра.

person Community    schedule 17.10.2008
comment
+1, но учтите, что при повторной компиляции могут возникнуть проблемы с производительностью. Я стараюсь сначала попробовать вариант №4 ... - person Marc Gravell; 18.10.2008
comment
Насколько я понимаю, тип соединения (слияние / хэш / цикл) выбирается на основе двух основных факторов: 1) индексы для объединенных столбцов 2) статистика, которая предсказывает размер соединения. Таким образом, каждый раз, когда вы запускаете запрос, в зависимости от предполагаемого размера соединения он выбирает подходящее соединение. Запекается ли выбор соединения в скомпилированном запросе? Т.е. если анализ параметров плохо предсказывает типичный размер соединений, будет ли он внесен в план запроса плохо выбранный тип соединения? - person AaronLS; 22.05.2014

Простой способ ускорить это - переназначить входные параметры локальным параметрам в самом начале sproc, например

CREATE PROCEDURE uspParameterSniffingAvoidance
    @SniffedFormalParameter int
AS
BEGIN

    DECLARE @SniffAvoidingLocalParameter int
    SET @SniffAvoidingLocalParameter = @SniffedFormalParameter

    --Work w/ @SniffAvoidingLocalParameter in sproc body 
    -- ...
person 6eorge Jetson    schedule 19.10.2008
comment
Это решение сократило время выполнения моего запроса на 50%. Не идеально, но лучше! - person Davide Vosti; 12.12.2008
comment
Но почему все равно помогает? - person Tim Büthe; 30.06.2011
comment
@ TimBüthe: он работает, не позволяя SQL Server кэшировать (или использовать кешированную версию) плана запроса для вашей процедуры. В зависимости от значения вашего параметра один план выполнения запроса может быть быстрее или медленнее другого. При первом запуске процедуры она построит план на основе значения этого первого параметра. Переназначение его локально заставит SQL Server каждый раз использовать новый план. - person Cᴏʀʏ; 18.10.2011
comment
@Cory Я не верю, что то, что вы сказали, верно. Это предотвращает обнюхивание, но не предотвращает кэширование плана. Вместо этого он просто приводит к кэшированию общего плана (IE не для определенного значения параметра). - person JohnOpincar; 11.09.2012

По моему опыту, лучшим решением для сниффинга параметров является «Динамический SQL». Следует отметить две важные вещи: 1. вы должны использовать параметры в своем динамическом sql-запросе 2. вы должны использовать sp_executesql (а не sp_execute), который сохраняет план выполнения для каждого значения параметра.

person Sadhir    schedule 09.11.2010
comment
В мире атак с использованием SQL-инъекций я не думаю, что рекомендовать динамический SQL - хорошая идея. - person datagod; 05.10.2011
comment
Он также рекомендовал использовать параметры, чтобы избежать внедрения SQL-кода. - person Mason G. Zhwiti; 27.10.2011
comment
Я не думаю, что это тот случай, когда мы используем динамический sql внутри процедуры. Нахождение параметров невозможно решить, если мы используем динамический sql и sp_executesql. sp_executesql с кешированием первого плана выполнения. Если мы используем exec, он генерирует другой план для разных параметров. - person Pavan Kumar Aryasomayajulu; 30.09.2020

Анализ параметров - это метод, который SQL Server использует для оптимизации плана выполнения запроса для хранимой процедуры. Когда вы впервые вызываете хранимую процедуру, SQL Server просматривает заданные значения параметров вашего вызова и решает, какие индексы использовать, на основе значений параметров.

Поэтому, когда первый вызов содержит нетипичные параметры, SQL Server может выбрать и сохранить неоптимальный план выполнения в отношении следующих вызовов хранимой процедуры.

Вы можете обойти это, либо

  • используя WITH RECOMPILE
  • копирование значений параметров в локальные переменные внутри хранимой процедуры и использование локальных переменных в ваших запросах.

Я даже слышал, что лучше вообще не использовать хранимые процедуры, а отправлять запросы прямо на сервер. Недавно я столкнулся с той же проблемой, решения которой у меня пока нет. Для некоторых запросов копирование в локальные вары помогает вернуться к правильному плану выполнения, для некоторых запросов производительность ухудшается из-за локальных варов.

Мне все еще нужно провести дополнительные исследования того, как SQL Server кэширует и повторно использует (неоптимальные) планы выполнения.

person Jan    schedule 22.10.2008

У меня была аналогичная проблема. План выполнения моей хранимой процедуры занял 30-40 секунд. Я попытался использовать операторы SP в окне запроса, и для их выполнения потребовалось несколько мс. Затем я разработал объявление локальных переменных внутри хранимой процедуры и перенос значений параметров в локальные переменные. Это сделало выполнение SP очень быстрым, и теперь тот же SP выполняется в течение нескольких миллисекунд вместо 30-40 секунд.

person Khuzaima Shajapurwala    schedule 26.03.2013

Очень простой и удобный оптимизатор запросов использует старый план запроса для часто выполняемых запросов. но на самом деле размер данных также увеличивается, поэтому в это время требуется новый оптимизированный план, и оптимизатор запросов по-прежнему использует старый план запроса. Это называется анализом параметров. Я также написал подробный пост по этому поводу. Посетите этот URL: http://www.dbrnd.com/2015/05/sql-server-parameter-sniffing/

person Anvesh    schedule 21.08.2015

Изменение процедуры хранения для выполнения в виде партии должно увеличить скорость.

Выбор пакетного файла, т.е .:

exec ('select * from order where  order id ='''+ @ordersID')

Вместо обычной хранимой процедуры выберите:

select * from order where  order id = @ordersID

Просто передайте параметр как nvarchar, и вы получите более быстрые результаты.

person katlego.nkosi    schedule 17.07.2012