Я пытаюсь понять проблему, которая возникает у нас с SQL Server 2000. Мы являемся веб-сайтом с умеренными транзакциями, и у нас есть хранимый процесс, sp_GetCurrentTransactions
который принимает идентификатор клиента и две даты.
Теперь, в зависимости от даты и клиента, этот запрос может возвращать от нуля до 1000 строк.
Проблема: мы столкнулись с тем, что внезапно мы получим ряд ошибок (обычно Execution Timeout Expired
или аналогичных) для конкретного клиента, пока они пытаются выполнить этот сохраненный процесс. Итак, мы изучаем запрос, запускаем его в SSMS и обнаруживаем, что он занимает 30 секунд. Таким образом, мы перекомпилируем хранимый процесс и -bang- теперь он работает за 300 мс.
Я говорил с нашим администратором базы данных об этом. Он сказал мне, что база данных создала план запроса, когда мы создали хранимый процесс. Он сказал, что это хороший план для этого набора параметров, но если вы добавите в него определенный набор параметров, тогда этот план не будет лучшим планом для этих данных, и поэтому вы увидите, что он работает медленно.
Варианты, представленные мне, - это перемещение этого проблемного запроса из хранимого процесса и обратно в динамический SQL, в котором план выполнения создается при каждом запуске.
Это похоже на шаг назад ко мне, и я чувствую, что должен быть способ обойти это. Есть ли другой способ решить эту проблему?
Любые ответы приветствуются.
источник
Ответы:
Эта проблема называется анализом параметров.
Более поздние версии SQL Server предоставляют больше возможностей для работы с ним, например
OPTION (RECOMPILE)
илиOPTIMIZE FOR
подсказки.Вы можете попытаться объявить переменные в хранимой процедуре, назначить значения параметров переменным и использовать переменные вместо параметров, как это звучит, как если бы большую часть времени вы получали достаточно удовлетворительный план.
Обычно наиболее катастрофически плохие планы составляются для параметров с очень высокой селективностью, но выполняются с параметрами с низкой избирательностью.
Если предположить, что сгенерированный план является более надежным с этим подходом и удовлетворительным для всех значений параметров, то преимущество этого подхода по сравнению с предложенным JNK состоит в том, что он не требует затрат на компиляцию для каждого вызова.
Недостатком является то, что для некоторых исполнений время выполнения может быть больше, чем для плана, специально предназначенного для этих значений параметров, поэтому это компромисс между временем компиляции и временем выполнения.
источник
Вместо того, чтобы использовать динамический SQL, вы всегда можете просто изменить ваши вызовы процедур на:
EXEC Database.dbo.usp_Myprocedure 'Parameter' WITH RECOMPILE
Эти
WITH RECOMPILE
силы ( как вы уже догадались!) Перекомпиляцией плана выполнения , когда она выполняется.Вы также можете включить
WITH RECOMPILE
в определение хранимой процедуры:источник
Вы также можете попытаться выбрать базу данных, которую планируете использовать, хотя вам придется немного поработать с оптимизатором, поэтому она более хрупкая, чем вы надеетесь.
Техника такова - разбить хранимую процедуру на 2, один предназначен для одного набора параметров, один для другого. Добавьте к каждому из них пункты where, чтобы между ними охватывались все возможные случаи. Посмотрите на планы запросов - один должен быть оптимизирован для одного набора параметров, другой для другого набора. Возможно, вам придется повозиться с запросом, чтобы это произошло, или это может оказаться невозможным для вашего запроса, и в этом случае этот подход не будет работать.
Теперь сделайте вашу оригинальную хранимую процедуру, проверьте значения параметров и отправьте в соответствующую одну из двух хранимых процедур из предыдущего абзаца.
Это может сработать, но это своего рода хак, чтобы заставить оптимизатор работать более эффективно для вашего запроса. Как и все подобные взломы, в будущих версиях базы данных это может оказаться ненужным или даже усугубить ситуацию. Поэтому, даже если это работает, вы должны решить, стоит ли это того.
источник
Вы также можете попробовать
SET FORCEPLAN
и индексировать подсказки.http://msdn.microsoft.com/en-us/library/ms188344.aspx
Это в основном позволяет вам выбрать порядок, в котором происходит объединение.
У вас могут быть подсказки индекса, чтобы гарантировать, что сервер SQL использует правильные индексы.
источник
Хм ... если бы мы сосредоточились только на этой хранимой процедуре, я был бы удивлен, что использование кэшированного плана выполнения вызвало бы проблему, которую вы видите. Я бы попросил посмотреть план выполнения хранимой процедуры, используя набор параметров для клиента и две даты. Интересно, был бы полезен более конкретный индекс -> такой как на customerId, и только две даты?
источник
Внезапно снижающаяся производительность звучит как неэффективный план запроса, который создается, вероятно, в результате отсутствия статистики. Запустите профилировщик SQL Server с установленными категориями событий «Ошибки и предупреждения» и посмотрите, есть ли предупреждения об отсутствии статистики.
Возможно, вам также не хватает индекса, или вам может потребоваться выполнить дефрагментацию индексов, поскольку они могут быть слишком фрагментированы для использования SQL Server, что приводит к мысли, что сканирование таблицы будет производить меньше операций ввода-вывода.
@JNK поднимает вопрос о хранимых процессах - они компилируются заранее, и план запроса будет сохранен вместе с хранимой процедурой.
Я не обязательно согласен с использованием WITH RECOMPILE, так как вы потеряете преимущество сохранения и повторного использования плана запросов. В некоторых случаях это необходимо - т.е. если ваша статистика распределения в базовых таблицах сильно различается между вызовами, но, как правило, после того, как данные в таблицах являются зрелыми, распределение данных в таблицах будет меняться минимально.
Итак, подведем итог:
источник