Сохраненная процедура медленная при вызове из Интернета, быстрая из Management Studio

97

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

Я запустил Sql Profiler и отследил вызовы, которые истекли, и наконец обнаружил следующие вещи:

  1. При выполнении операторов из MS SQL Management Studio с теми же аргументами (фактически, я скопировал вызов процедуры из трассировки профиля sql и запустил его): он завершается через 5 ~ 6 секунд в среднем.
  2. Но при вызове из веб-приложения это занимает более 30 секунд (в трассировке), поэтому моя веб-страница к тому времени фактически истекает.

Помимо того, что у моего веб-приложения есть собственный пользователь, все одинаково (та же база данных, соединение, сервер и т. Д.). Я также пробовал запускать запрос непосредственно в студии с пользователем веб-приложения, и это занимает не более 6 сек.

Как узнать, что происходит?

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

ИЗМЕНИТЬ В этой ссылке я узнал, что ADO.NET устанавливает ARITHABORTзначение true - что хорошо в большинстве случаев, но иногда такое случается, и предлагаемое решение - добавить with recompileпараметр в сохраненный процесс. В моем случае это не работает, но я подозреваю, что это очень похоже на это. Кто-нибудь знает, что еще делает ADO.NET или где я могу найти спецификацию?

я серьезно
источник
1
Это может быть связано с тем, сколько данных возвращается?
Barry Kaye
@Barry: Нет, поскольку я запускаю ту же процедуру (тоже скопированную из трассировки - что означает те же параметры) в студии управления, она выполняется в течение 6 секунд.
iamserious 05
@Jayantha: Дело НЕ в том, что sp работает медленно, а в ЧЕМ-то между ado.net и sql. Я не понимаю, какое значение имеет sp.
iamserious 05
2
Возвращает ли SP много данных, например столбцы image / text / varchar (max)? Объем данных, потребляемых клиентом, будет огромным, что займет много времени. SSMS отсекает эти наборы результатов более эффективно.
Тим Шмелтер
1
Возможный дубликат stackoverflow.com/questions/2248112/…
Аарон Бертран

Ответы:

79

У меня возникала аналогичная проблема в прошлом, поэтому я очень хочу увидеть решение этого вопроса. Комментарий Аарона Бертрана к OP привел к тому, что время ожидания запроса истекло при выполнении из Интернета, но очень быстро при выполнении из SSMS , и хотя вопрос не является дубликатом, ответ вполне может относиться к вашей ситуации.

По сути, похоже, что SQL Server может иметь поврежденный кэшированный план выполнения. Вы попадаете в плохой план с вашим веб-сервером, но SSMS переходит на другой план, поскольку для флага ARITHABORT существует другой параметр (который в противном случае не повлиял бы на ваш конкретный запрос / сохраненный процесс).

См. Еще один пример, в котором вызов хранимой процедуры T-SQL в ADO.NET вызывает исключение SqlTimeoutException с более полным объяснением и разрешением.

СтриптизВоин
источник
Это несколько интересных зацепок, я узнаю о них больше и вернусь через несколько ... спасибо.
iamserious 05
44
Привет, очистил кеш DBCC DROPCLEANBUFFERSи DBCC FREEPROCCACHEсвое дело! Я предполагаю, что план выполнения был каким-то образом поврежден или не обновлен. Я сомневаюсь, что это постоянное исправление, если оно могло быть повреждено однажды, оно могло бы сделать это снова. Так что я все еще ищу вероятные причины. Поскольку веб-приложение снова заработало, мне не нужно чувствовать давление. Большое спасибо.
iamserious 06
2
@iamserious - спасибо! После изменения моей хранимой процедуры у меня возникла проблема с тайм-аутом. Затем я выполнил две упомянутые выше команды DBCC, и проблема была решена.
Induster
5
@Mangist: поскольку это сложная проблема, серебряной пули нет, но вы можете попробовать использовать OPTIMIZE FORили OPTION(Recompile)запрашивать подсказки по запросам, вызывающим проблемы. В этой статье подробно рассматривается проблема. Если Entity Framework генерирует ваши запросы, этот пост, по- видимому, предоставляет способ добавить подсказку к вашим запросам.
StriplingWarrior
8
Вместо запуска DBCC DROPCLEANBUFFERS и DBCC FREEPROCCACHE, которые очистят ВСЕ планы выполнения, вы можете отбросить и воссоздать проблемную хранимую процедуру.
jnoreiga
51

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

Для меня исправление заключалось в том, чтобы изменить все параметры, которые используются в sproc, на локальные переменные.

например. изменение:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
SELECT * FROM Table WHERE ID = @param1 

кому:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
DECLARE @param1a int
SET @param1a = @param1
SELECT * FROM Table WHERE ID = @param1a

Кажется странным, но это устранило мою проблему.

Зейн
источник
3
Вау, у меня была та же проблема, и я не верил, что это сработает, но это сработало.
Lac Ho
1
Зейн, ты знаешь, решит ли это проблему навсегда или она может вернуться? Спасибо!
Lac Ho
1
После внесения этого изменения у меня не было проблем со скоростью хранимой процедуры.
Zane
1
Вау, это тоже устранило мою проблему! Это ДОЛЖНО быть ошибкой в ​​sql server. Зачем заставлять разработчиков sql прыгать через этот глупый обруч, когда MSSQL может внутренне преобразовывать в локальный код под капотом для ОГРОМНОГО увеличения производительности?
HerrimanCoder
3
Может ли быть, что изменение процедуры приводит к созданию нового плана выполнения, поэтому решение на самом деле не копирует входную переменную в локальную переменную, а просто форсирует создание нового плана выполнения (как объяснено в принятом решении)?
Garland Pope
10

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

Я попытался настроить перекомпиляцию хранимой процедуры с помощью, sp_recompileи это решило проблему для одного SP.

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

Клинт
источник
1
Спасибо за это. Пометка процедуры перекомпиляции с помощью команды sp_recompile у меня сработала, когда DBCC DROPCLEANBUFFERSи DBCC FREEPROCCACHEне имело значения.
Стив,
4

Может ли быть, что некоторые другие вызовы БД, сделанные до того, как веб-приложение вызовет SP, сохраняют транзакцию открытой? Это могло быть причиной того, что SP будет ждать при вызове веб-приложением. Я предлагаю изолировать вызов в веб-приложении (поместить его на новую страницу), чтобы убедиться, что некоторые предыдущие действия в веб-приложении вызывают эту проблему.

Тундей
источник
1
Привет, @Tundey, я изолировал звонок, и он все еще занимает около 30 секунд и время ожидания истекает. так что это должно быть что-то среднее между общением, я думаю?
iamserious 05
1

Простая перекомпиляция хранимой процедуры (табличная функция в моем случае) сработала для меня

Гай Бибер
источник
1

как @Zane сказал, что это может быть из-за обнюхивания параметров. Я испытал такое же поведение, и я взглянул на план выполнения процедуры и все операторы sp в строке (скопировал все операторы из процедуры, объявил параметры как переменные и присвоил переменной те же значения, что и параметры были). Однако план казни выглядел совершенно иначе. Выполнение sp заняло 3-4 секунды, и сразу же были возвращены следующие подряд операторы с точно такими же значениями.

план исполнения

После некоторого поиска в Google я нашел интересную статью об этом поведении: медленно в приложении, быстро в SSMS?

При компиляции процедуры SQL Server не знает, что значение @fromdate изменяется, но компилирует процедуру в предположении, что @fromdate имеет значение NULL. Поскольку все сравнения с NULL дают значение UNKNOWN, запрос вообще не может возвращать никаких строк, если @fromdate все еще имеет это значение во время выполнения. Если SQL Server примет входное значение как окончательную истину, он может построить план только с постоянным сканированием, которое вообще не обращается к таблице (запустите запрос SELECT * FROM Orders WHERE OrderDate> NULL, чтобы увидеть пример этого) . Но SQL Server должен создать план, который вернет правильный результат, независимо от того, какое значение @fromdate имеет во время выполнения. С другой стороны, нет никаких обязательств по созданию плана, который наилучшим образом соответствовал бы всем ценностям. Таким образом, поскольку предполагается, что никакие строки не будут возвращены, SQL Server соглашается на поиск индекса.

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

create procedure dbo.procedure
    @dateTo datetime = null
begin
    if (@dateTo is null)
    begin
        select @dateTo  = GETUTCDATE()
    end

    select foo
    from dbo.table
    where createdDate < @dateTo
end

После того, как я изменил его на

create procedure dbo.procedure
    @dateTo datetime = null
begin
    declare @to datetime = coalesce(@dateTo, getutcdate())

    select foo
    from dbo.table
    where createdDate < @to
end 

это снова сработало как шарм.

TomPez
источник