Почему оптимизатор SqlServer так запутывается с параметрами? - PullRequest
7 голосов
/ 05 января 2009

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

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

Я просто исправил такую ​​процедуру, преобразовав это:

alter procedure p_MyProc
(
    @param1 int
) as -- do a complex query with @param1

к этому:

alter procedure p_MyProc
(
    @param1 int
)
as

declare @param1Copy int;
set @param1Copy = @param1;

-- Do the query using @param1Copy

Он перешел от бега более минуты назад к менее чем одной секунде, как это обычно происходит. Такое поведение кажется совершенно случайным. Для 9 из 10 входных данных @ param1 запрос выполняется быстро, независимо от того, сколько данных ему нужно обработать, или насколько велик результат, заданный им. Но для этого 1 из 10, это просто теряется. И исправление состоит в том, чтобы заменить int на тот же int в запросе?

Это не имеет смысла.

[Изменить]

@ gbn связан с этим вопросом, в котором подробно описана похожая проблема:

Известная проблема ?: Не удается завершить хранимую процедуру SQL Server 2005 с параметром

Стесняюсь плакать "Жук!" потому что это часто отговорка, но это действительно кажется мне ошибкой. Когда я запускаю две версии моей хранимой процедуры с одним и тем же вводом, я вижу одинаковые планы запросов. Разница лишь в том, что запуск оригинала занимает больше минуты, а версия с тупым копированием параметров запускается мгновенно.

Ответы [ 9 ]

5 голосов
/ 05 января 2009

1 из 10 дает неверный план, который кэшируется.

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

По неверному плану, что если 1 из 10 генерирует сканирование по индексу 1, а остальные 9 производят поиск по индексу 2? например, 1 из 10, скажем, 50% строк?

Редактировать: другие вопросы

Редактировать 2:

Перекомпиляция не работает, потому что параметры прослушиваются во время компиляции.
Из других ссылок (вставлено в):

Эта статья объясняет ...

...parameter values are sniffed during compilation or recompilation...

Наконец (редактировать 3):

Анализ параметров был, вероятно, хорошей идеей в то время и, вероятно, в основном работает хорошо. Мы используем его по всем направлениям для любого параметра, который в итоге окажется в предложении WHERE. Нам не нужно его использовать, потому что мы знаем, что только несколько (более сложные, например, отчеты или много параметров) могут вызвать проблемы, но мы используем его для согласованности.

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

2 голосов
/ 06 января 2009

Я неоднократно сталкивался с этой проблемой при переносе своего кода с тестового сервера в рабочий режим - в двух разных сборках SQL Server 2005 . Я думаю, что есть некоторые большие проблемы с перехватом параметров в некоторых сборках SQL Server 2005. У меня никогда не было этой проблемы на сервере dev или на двух локальных выпусках разработчика. Я никогда не видел, чтобы это было такой большой проблемой на SQL Server 2000 или любой версии, возвращающейся к 6.5.

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

  • с помощью подсказки WITH RECOMPILE на EXEC или в самом SP.
  • сброс и воссоздание SP
  • с использованием sp_recompile

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

Да, и если SQL Server не может справиться с этим без маскировки, ему нужно добавить модификатор параметра NOSNIFF или что-то еще. Что произойдет, если вы замаскируете все свои параметры, поэтому у вас есть @Something_parm и @Something_var, и кто-то изменяет код, чтобы использовать неправильный код, и вдруг у вас снова возникает проблема с прослушиванием? Кроме того, вы загрязняете пространство имен в SP. Все эти SP, которые я «чиню», сводят меня с ума, потому что я знаю, что они станут кошмаром технического обслуживания для менее опытных сотрудников. Я передам этот проект на один день.

1 голос
/ 06 января 2009

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

1 голос
/ 05 января 2009

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

EXECUTE MyProcedure [parameters] WITH RECOMPILE
Параметр

WITH RECOMPILE заставит SQL Server игнорировать кэшированный план.

0 голосов
/ 26 апреля 2013

Я знаю, что это 2-летняя тема, но она может помочь кому-то в дальнейшем.

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

SELECT col1, col2, col3
FROM YourTableHere WITH (INDEX (PK_YourIndexHere))

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

0 голосов
/ 06 января 2009

Является ли каждый запрос ссылкой на параметр, сравнивающий его со значениями типа int, без функций и без приведения?

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

0 голосов
/ 06 января 2009

Это проблема с кэшированием плана, и она не всегда связана с параметрами, как это было в вашем сценарии.

(Проблемы с перехватом параметров возникают, когда процесс вызывается с необычными параметрами в ПЕРВЫЙ раз, когда он выполняется, и поэтому кэшированный план отлично работает для этих нечетных значений, но в большинстве других случаев он вызывает жалость.)

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

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

Нашим исправлением было принудительное перекомпиляция с помощью sp_recompile.

0 голосов
/ 06 января 2009

Есть ли вероятность, что предоставляемое значение параметра иногда не является целым?

0 голосов
/ 05 января 2009

Как указано, это проблема компиляции. Эта проблема все еще возникает, если вы отмените процедуру? Одна вещь, которую вы можете попробовать, если это произойдет снова для принудительной перекомпиляции, это использовать:

sp_recompile [@objname =] 'object'

Справа от BOL в отношении параметра @objname:

Полное или неквалифицированное имя хранимой процедуры, триггера, таблицы или представления в текущей базе данных. объект nvarchar (776), без значения по умолчанию. Если объект является именем хранимой процедуры или триггера, хранимая процедура или триггер будет перекомпилирована при следующем запуске. Если объект является именем таблицы или представления, все хранимые процедуры, которые ссылаются на таблицу или представление, будут перекомпилированы при следующем запуске.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...