2008-10-17 2 views
60

Некоторое время назад у меня был запрос, который я довольно много использовал для одного из моих пользователей. Он все еще эволюционировал и настраивался, но в итоге он был стабилизирован и работал довольно быстро, поэтому мы создали из него хранимую процедуру.Параметр Sniffing (или Spoofing) в SQL Server

До сих пор такой нормальный.

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

[Background, мы бежим SQL Server 2005.]

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

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

Итак, что дает? Может ли кто-то объяснить параметризацию спуфинга?

Bonus кредит на

  • подсветка, как избежать этого
  • указывает, как распознать ВОЗМОЖНАЯ ПРИЧИНА
  • обсудить альтернативные стратегии, например, статистика, индексы, ключи, для смягчения ситуации
+0

Это не просто возможность, это определенность - нет такой вещи, как подделка параметров. Это параметр sniffing. – ErikE

ответ

52

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

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

usp_QueryMyDataByState 'Rhode Island' 

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

usp_QueryMyDataByState 'Texas' 

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

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

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

+2

так .... как этого избежать? – thomaspaulb

+3

Подберите грант Отличную книгу Гранта Фричей SQL Server 2008 Query Performance Дистиллируйте, где он просматривает все ваши варианты. Несмотря на то, что он говорит о 2008 году, это здорово и для 2005 года. –

+0

Я рекомендую посмотреть два возможных решения, первое, что я пробовал и видел, второе, что я подозреваю, работаю, но на практике не пробовал. –

26

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

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

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

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

+1

+1, но обратите внимание, что «с перекомпиляцией» могут иметь проблемы с производительностью самого себя. Я предпочитаю сначала попробовать вариант №4 ... –

+0

Мое понимание соединений - это тип объединения (объединение/хэш/цикл), который выбирается на основе двух основных факторов: 1) индексов на объединенных столбцах; 2) статистики, которые прогнозируют размер соединения. Поэтому каждый раз, когда вы запускаете запрос, в зависимости от предполагаемого размера соединения, он выбирает соответствующее соединение. Является ли выбор соединения запеченным в скомпилированный запрос? То есть если параметр sniffing плохо прогнозирует типичный размер объединений, будет ли он запекать в плане запроса плохо выбранный тип соединения? – AaronLS

24

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

CREATE PROCEDURE uspParameterSniffingAvoidance 
    @SniffedFormalParameter int 
AS 
BEGIN 

    DECLARE @SniffAvoidingLocalParameter int 
    SET @SniffAvoidingLocalParameter = @SniffedFormalParameter 

    --Work w/ @SniffAvoidingLocalParameter in sproc body 
    -- ... 
+1

Это решение сократило время выполнения моего запроса на 50%. Не идеально, но лучше! –

+0

Но _why_ все равно помогает? –

+1

@ TimBüthe: Он работает, предотвращая кеширование SQL Server (или использование кешированной версии) плана запроса для вашей процедуры. В зависимости от значения вашего параметра один план выполнения запроса может быть быстрее или медленнее другого. При первом запуске процедуры будет построен план, основанный на значении первого параметра. При повторном назначении локально SQL Server будет использовать новый план каждый раз. –

4

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

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

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

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

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

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

4

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

+0

В мире SQL Инъекционные атаки, я не думаю, что рекомендации по динамическому SQL - хорошая идея. – datagod

+1

Он также рекомендовал использовать параметры, которые позволяют избежать SQL-инъекций. –

-2

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

Пакетный файл выберите т.е .:

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

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

select * from order where order id = @ordersID 

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

0

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

-1

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

Смежные вопросы