ИЗМЕНЕНИЕ хранимой процедуры (без изменений) приводит к ее запуску в 20 раз медленнее

#sql #sql-server #sql-server-2017

#sql #sql-сервер #sql-server-2017

Вопрос:

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

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

Вот процесс:

  1. Удалите все данные из целевой таблицы
  2. Запустите хранимую процедуру и передайте запись JSON из 50 000 строк
  3. Она выполняется примерно за 1,5 секунды.
  4. Повторите процесс. На этот раз у нее есть существующие данные, которые необходимо проанализировать в поисках дубликатов. Те же результаты. Менее 2 секунд
  5. Повторите шаг 4 N раз, всегда с одинаковыми результатами.
  6. Запустите ALTER в SP, не внося НИКАКИХ изменений в сам SP
  7. Повторите шаг 4. На этот раз это занимает 30-40 секунд!!!
  8. Удалите данные в целевой таблице, повторите все шаги, результаты те же.

Я читал об анализе параметров, пробовал такие вещи, как преобразование переданных параметров в локальные параметры и добавление WITH RECOMPILE , но пока результаты все те же.

Если бы это происходило в prod, это было бы неприемлемо. У кого-нибудь есть какие-нибудь идеи?

Спасибо!

Ответ №1:

Это немного длинновато для комментария.

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

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

По какой-то причине этот второй план запроса намного хуже первого. Я не знаю почему. Обычно проблема заключается в обратном (план запроса к пустой таблице хуже).

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

Комментарии:

1. хорошо, хорошо. Это имеет смысл! Как перекомпилировать код в хранимой процедуре при каждом ее запуске?

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