Procedura składowana wolna w przypadku wywołania z sieci WWW, szybka z Management Studio

97

Mam procedurę składowaną, która niesamowicie przekracza limit czasu za każdym razem, gdy jest wywoływana z aplikacji internetowej.

Uruchomiłem Sql Profiler i prześledziłem wywołania, które wygasły, i w końcu odkryłem następujące rzeczy:

  1. Po wykonaniu instrukcji z poziomu MS SQL Management Studio, z tymi samymi argumentami (w rzeczywistości skopiowałem wywołanie procedury ze śledzenia profilu sql i uruchomiłem je): Kończy się średnio za 5 ~ 6 sekund.
  2. Jednak wywołanie z aplikacji internetowej zajmuje ponad 30 sekund (w śledzeniu), więc moja strona internetowa faktycznie wygasa do tego czasu.

Poza tym, że moja aplikacja internetowa ma własnego użytkownika, wszystko jest takie samo (ta sama baza danych, połączenie, serwer itp.) Próbowałem również uruchomić zapytanie bezpośrednio w studio z użytkownikiem aplikacji internetowej i nie zajmuje to więcej niż 6 sek.

Jak dowiedzieć się, co się dzieje?

Zakładam, że nie ma to nic wspólnego z faktem, że używamy warstw BLL> DAL lub adapterów tabeli, ponieważ ślad wyraźnie pokazuje, że opóźnienie występuje w rzeczywistej procedurze. To wszystko, o czym mogę myśleć.

EDYCJA Dowiedziałem się z tego linku, że ADO.NET ustawia wartość ARITHABORTna true - co jest dobre przez większość czasu, ale czasami tak się dzieje, a sugerowanym obejściem jest dodanie with recompileopcji do przechowywanego proc. W moim przypadku to nie działa, ale podejrzewam, że jest to coś bardzo podobnego do tego. Czy ktoś wie, co jeszcze robi ADO.NET lub gdzie mogę znaleźć specyfikację?

jestem poważny
źródło
1
Może to być związane z ilością zwracanych danych?
Barry Kaye,
@Barry: Nie, ponieważ wykonuję tę samą procedurę (skopiowaną również ze śledzenia - czyli te same parametry) w Management Studio, działa ona w ciągu 6 sekund.
iamserious
@Jayantha: Nie chodzi o to, że sp jest wolny, ale COŚ między ado.net i sql jest. Nie rozumiem, jaką różnicę miałoby sp.
iamserious
2
Czy SP zwraca dużo danych, na przykład kolumny image / text / varchar (max)? Ilość danych do pobrania na kliencie byłaby ogromna, co zajmie dużo czasu. SSMS odcina te zbiory wyników w bardziej efektywny sposób.
Tim Schmelter,
1
Możliwy duplikat stackoverflow.com/questions/2248112/…
Aaron Bertrand

Odpowiedzi:

79

W przeszłości miałem podobny problem, więc nie mogę się doczekać rozwiązania tego pytania. Komentarz Aarona Bertranda na temat OP doprowadził do przekroczenia limitu czasu Query podczas wykonywania z sieci, ale bardzo szybkiego w przypadku wykonywania z SSMS i chociaż pytanie nie jest powtórzeniem, odpowiedź może bardzo dobrze dotyczyć Twojej sytuacji.

Zasadniczo wygląda na to, że SQL Server może mieć uszkodzony plan wykonania w pamięci podręcznej. Uderzasz w zły plan ze swoim serwerem sieciowym, ale SSMS ląduje na innym planie, ponieważ jest inne ustawienie flagi ARITHABORT (która w przeciwnym razie nie miałaby wpływu na twoje konkretne zapytanie / przechowywane proc).

Zobacz ADO.NET wywołanie procedury składowanej T-SQL powoduje SqlTimeoutException dla innego przykładu z pełniejszym wyjaśnieniem i rozwiązaniem.

StriplingWarrior
źródło
Oto kilka interesujących wskazówek, przeczytam o nich więcej i omówię za kilka ... dzięki.
iamserious
44
Cześć, wyczyściłem pamięć podręczną DBCC DROPCLEANBUFFERSi DBCC FREEPROCCACHEzałatwiłem sprawę! Domyślam się, że plan wykonania był w jakiś sposób uszkodzony lub nie został zaktualizowany. Wątpię, czy jest to trwała poprawka, gdyby raz mogła ulec uszkodzeniu, mogłaby to zrobić ponownie. Więc wciąż szukam prawdopodobnych przyczyn. Ponieważ aplikacja internetowa znów działa, nie muszę czuć presji. Dzięki wielkie.
iamserious
2
@iamserious - udało Ci się to, dzięki! Po zmianie procedury składowanej wystąpił problem z przekroczeniem limitu czasu. Następnie uruchomiłem dwa polecenia DBCC, o których wspomniałeś powyżej, i rozwiązało to problem.
Induster
5
@Mangist: Ponieważ jest to skomplikowany problem, nie ma srebrnej kuli, ale możesz spróbować użyć OPTIMIZE FORlub OPTION(Recompile)zapytać o wskazówki dotyczące zapytań, które powodują problemy. W tym artykule szczegółowo omówiono problem. Jeśli Entity Framework generuje zapytania, ten wpis wydaje się zapewniać sposób dodawania wskazówki dotyczącej zapytania do zapytań.
StriplingWarrior
8
Zamiast uruchamiać DBCC DROPCLEANBUFFERS i DBCC FREEPROCCACHE, które usuwają WSZYSTKIE plany wykonania, można porzucić i odtworzyć problematyczną procedurę składowaną.
jnoreiga
51

Doświadczam również, że zapytania działały powoli z sieci i szybko w SSMS i ostatecznie odkryłem, że problem był czymś, co nazywa się wykrywaniem parametrów.

Rozwiązaniem dla mnie była zmiana wszystkich parametrów używanych w sproc na zmienne lokalne.

na przykład. zmiana:

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

do:

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

Wydaje się dziwne, ale rozwiązało mój problem.

Zane
źródło
3
Wow, miałem ten sam problem i nie wierzyłem, że to zadziała, ale tak się stało.
Lac Ho
1
Zane, czy wiesz, czy to trwale rozwiąże problem, czy może powrócić? Dziękuję Ci!
Lac Ho
1
Od czasu wprowadzenia tej zmiany nie miałem żadnych problemów z szybkością procedury składowanej.
Zane,
1
Wow, to też rozwiązało mój problem! MUSI to być błąd w serwerze sql. Po co zmuszać twórców sql do przeskakiwania przez tę głupią pętlę, skoro MSSQL może wewnętrznie konwertować na lokalny pod maską, aby uzyskać OGROMNY wzrost wydajności?
HerrimanCoder
3
Czy to możliwe, że zmiana proc powoduje utworzenie nowego planu wykonania, więc rozwiązaniem nie jest tak naprawdę skopiowanie zmiennej wejściowej do zmiennej lokalnej, ale wymuszenie wygenerowania nowego planu wykonania (jak wyjaśniono w przyjętym rozwiązaniu)?
Garland Pope
10

Nie po to, by spamować, ale jako miejmy nadzieję pomocne rozwiązanie dla innych, nasz system doświadczył wielu przekroczeń czasu.

Próbowałem ustawić procedurę składowaną do ponownej kompilacji przy użyciu sp_recompilei rozwiązało to problem z jednym SP.

Ostatecznie istniała większa liczba SP, które przekraczały limit czasu, z których wiele nigdy wcześniej tego nie robiło, przez używanie, DBCC DROPCLEANBUFFERSa DBBC FREEPROCCACHEwskaźnik przekroczenia czasu incydentu znacznie spadł - nadal istnieją odosobnione zdarzenia, niektóre, w których podejrzewam, że trwa regeneracja planu jakiś czas, a niektóre, w których PS są naprawdę słabe i wymagają ponownej oceny.

Clint
źródło
1
Dzięki za to. Oznaczanie procedury ponownej kompilacji za pomocą polecenia sp_recompile działało dla mnie, gdy DBCC DROPCLEANBUFFERSi DBCC FREEPROCCACHEnie robiły różnicy.
Steve
4

Czy może być tak, że niektóre inne wywołania bazy danych wykonane przed wywołaniem aplikacji internetowej, SP utrzymują transakcję otwartą? Może to być powód, dla którego ten SP musi czekać na wywołanie przez aplikację internetową. Mówię, że wyodrębnij połączenie w aplikacji internetowej (umieść je na nowej stronie), aby upewnić się, że przyczyną tego problemu jest jakieś wcześniejsze działanie w aplikacji internetowej.

Tundey
źródło
1
Cześć @ Tundey, odizolowałem połączenie i nadal trwa to około 30 sekund i upływa limit czasu. więc to musi być coś pomiędzy komunikacją, jak sądzę?
iamserious
1

Po prostu rekompilacja procedury składowanej (w moim przypadku funkcja tabeli) zadziałała

Guy Biber
źródło
1

jak @Zane powiedział, że może to być spowodowane wykrywaniem parametrów. Doświadczyłem tego samego zachowania i przyjrzałem się planowi wykonania procedury i wszystkim oświadczeniom sp w wierszu (skopiowałem wszystkie instrukcje z procedury, zadeklarowałem parametry jako zmienne i przypisałem te same wartości do zmiennej jako parametry miały). Jednak plan wykonania wyglądał zupełnie inaczej. Wykonanie sp trwało 3-4 sekundy, a instrukcje z rzędu z dokładnie tymi samymi wartościami zostały natychmiast zwrócone.

plan wykonania

Po pewnym googlowaniu znalazłem interesującą lekturę na temat tego zachowania: Wolno w aplikacji, Szybko w SSMS?

Podczas kompilowania procedury SQL Server nie wie, że wartość @fromdate się zmienia, ale kompiluje procedurę przy założeniu, że @fromdate ma wartość NULL. Ponieważ wszystkie porównania z NULL dają UNKNOWN, zapytanie nie może w ogóle zwrócić żadnych wierszy, jeśli @fromdate nadal ma tę wartość w czasie wykonywania. Jeśli SQL Server przyjmie wartość wejściową jako ostateczną prawdę, może skonstruować plan tylko ze stałym skanowaniem, które w ogóle nie ma dostępu do tabeli (uruchom zapytanie SELECT * FROM Orders WHERE OrderDate> NULL, aby zobaczyć przykład) . Ale SQL Server musi wygenerować plan, który zwraca poprawny wynik bez względu na wartość @fromdate w czasie wykonywania. Z drugiej strony nie ma obowiązku budowania planu, który jest najlepszy dla wszystkich wartości. Tak więc, ponieważ założono, że żadne wiersze nie zostaną zwrócone, SQL Server decyduje się na przeszukiwanie indeksu.

Problem polegał na tym, że miałem parametry, które można było pozostawić puste i jeśli zostałyby przekazane jako null, zostałyby zainicjalizowane z wartością domyślną.

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

Po zmianie na

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

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

znowu zadziałało jak urok.

TomPez
źródło