Внезапно медленный план выполнения для хранимых процессов

15

Я пытаюсь понять проблему, которая возникает у нас с SQL Server 2000. Мы являемся веб-сайтом с умеренными транзакциями, и у нас есть хранимый процесс, sp_GetCurrentTransactionsкоторый принимает идентификатор клиента и две даты.

Теперь, в зависимости от даты и клиента, этот запрос может возвращать от нуля до 1000 строк.

Проблема: мы столкнулись с тем, что внезапно мы получим ряд ошибок (обычно Execution Timeout Expiredили аналогичных) для конкретного клиента, пока они пытаются выполнить этот сохраненный процесс. Итак, мы изучаем запрос, запускаем его в SSMS и обнаруживаем, что он занимает 30 секунд. Таким образом, мы перекомпилируем хранимый процесс и -bang- теперь он работает за 300 мс.

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

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

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

Любые ответы приветствуются.

Ciaran Archer
источник
в процедуре есть оператор if / else? Я видел, как это происходит, когда план кэшируется в операторе if, а затем пытается выполнить его в блоке else, используя неверный план. Соответствовали ли эти ошибки изменению процесса?
Джереми Грей
@ Джереми: Никаких изменений в proc и никаких утверждений else / if.
Ciaran Archer

Ответы:

14

Эта проблема называется анализом параметров.

Более поздние версии SQL Server предоставляют больше возможностей для работы с ним, например OPTION (RECOMPILE)или OPTIMIZE FORподсказки.

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

Обычно наиболее катастрофически плохие планы составляются для параметров с очень высокой селективностью, но выполняются с параметрами с низкой избирательностью.

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

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

Мартин Смит
источник
3
Или «приглядываться» в терминологии Oracle
Гай
Спасибо @Gaius, хорошо знать терминологию для нескольких СУБД;)
Андрей Ринеа
6

Вместо того, чтобы использовать динамический SQL, вы всегда можете просто изменить ваши вызовы процедур на:

EXEC Database.dbo.usp_Myprocedure 'Parameter' WITH RECOMPILE

Эти WITH RECOMPILEсилы ( как вы уже догадались!) Перекомпиляцией плана выполнения , когда она выполняется.

Вы также можете включить WITH RECOMPILEв определение хранимой процедуры:

CREATE PROCEDURE usp.MyProcedure (Parameters)
WITH RECOMPILE
AS
...
JNK
источник
2

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

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

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

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

PSR
источник
1

Вы также можете попробовать SET FORCEPLANи индексировать подсказки.

http://msdn.microsoft.com/en-us/library/ms188344.aspx
Это в основном позволяет вам выбрать порядок, в котором происходит объединение.

У вас могут быть подсказки индекса, чтобы гарантировать, что сервер SQL использует правильные индексы.

user606723
источник
0

Хм ... если бы мы сосредоточились только на этой хранимой процедуре, я был бы удивлен, что использование кэшированного плана выполнения вызвало бы проблему, которую вы видите. Я бы попросил посмотреть план выполнения хранимой процедуры, используя набор параметров для клиента и две даты. Интересно, был бы полезен более конкретный индекс -> такой как на customerId, и только две даты?

Брайан Найт
источник
2
Почему сюрприз? перехват параметров - довольно распространенная проблема с этими симптомами, и похоже, что администратор БД определил ее как проблему.
Мартин Смит
@MartinSmith - Я немного удивлен, что администратор базы данных, который знает о перехвате параметров, не знает о подсказках перекомпиляции ...
JNK
@JNK - Это правда. Не уверен, почему они не упомянули это.
Мартин Смит
0

Внезапно снижающаяся производительность звучит как неэффективный план запроса, который создается, вероятно, в результате отсутствия статистики. Запустите профилировщик SQL Server с установленными категориями событий «Ошибки и предупреждения» и посмотрите, есть ли предупреждения об отсутствии статистики.

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

@JNK поднимает вопрос о хранимых процессах - они компилируются заранее, и план запроса будет сохранен вместе с хранимой процедурой.

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

Итак, подведем итог:

  1. Проверьте отсутствие статистики
  2. Проверьте фрагментацию индекса
  3. Создать и использовать сохраненный процесс
  4. Переименуйте proc - sp_ - это мягко зарезервированное пространство имен префиксов для внутренних системных протоколов SQL Server, в результате чего SQL Server всегда сначала ищет в базе данных master эти хранимые процедуры. Переименование proc usp_ вместо sp_ приведет к увеличению производительности, но я сомневаюсь, что это ваша проблема в этом случае.
IFX
источник