Gespeicherte Prozedur langsam, wenn sie aus dem Web aufgerufen wird, schnell aus Management Studio

97

Ich habe eine Prozedur gespeichert, die jedes Mal, wenn sie von der Webanwendung aufgerufen wird, eine wahnsinnige Zeitüberschreitung aufweist.

Ich habe den SQL-Profiler gestartet und die Anrufe in dieser Zeit verfolgt und schließlich Folgendes herausgefunden:

  1. Bei der Ausführung der Anweisungen aus dem MS SQL Management Studio mit denselben Argumenten (tatsächlich habe ich den Prozeduraufruf aus dem SQL-Profil-Trace kopiert und ausgeführt): Der Vorgang dauert durchschnittlich 5 bis 6 Sekunden.
  2. Wenn es von einer Webanwendung aufgerufen wird, dauert es mehr als 30 Sekunden (in Trace), sodass meine Webseite bis dahin tatsächlich eine Zeitüberschreitung aufweist.

Abgesehen von der Tatsache, dass meine Webanwendung einen eigenen Benutzer hat, ist alles gleich (dieselbe Datenbank, Verbindung, Server usw.). Ich habe auch versucht, die Abfrage direkt im Studio mit dem Benutzer der Webanwendung auszuführen, und es dauert nicht mehr als 6 sek.

Wie finde ich heraus, was passiert?

Ich gehe davon aus, dass dies nichts mit der Tatsache zu tun hat, dass wir BLL> DAL-Schichten oder Tabellenadapter verwenden, da die Kurve deutlich zeigt, dass die Verzögerung im tatsächlichen Verfahren liegt. Das ist alles woran ich denken kann.

BEARBEITEN Ich habe in diesem Link herausgefunden, dass ADO.NET ARITHABORTauf true gesetzt ist - was für die meiste Zeit gut ist, aber manchmal passiert dies, und die vorgeschlagene Problemumgehung besteht darin with recompile, dem gespeicherten Prozess eine Option hinzuzufügen . In meinem Fall funktioniert es nicht, aber ich vermute, es ist etwas sehr Ähnliches. Weiß jemand, was ADO.NET sonst noch macht oder wo ich die Spezifikation finden kann?

ich meine es ernst
quelle
1
Dies könnte damit zusammenhängen, wie viele Daten zurückgegeben werden.
Barry Kaye
@Barry: Nein, da ich im Management Studio dieselbe Prozedur ausführe (auch aus dem Trace kopiert - dh dieselben Parameter), wird sie innerhalb von 6 Sekunden ausgeführt.
iamserious
@ Jayantha: Der Punkt ist NICHT, dass die SP langsam ist, aber etwas zwischen ado.net und SQL ist. Ich sehe nicht, wie der SP einen Unterschied machen würde.
iamserious
2
Gibt der SP viele Daten zurück, z. B. Bild- / Text- / Varchar-Spalten (max)? Die Datenmenge, die auf dem Client verbraucht werden muss, ist enorm, was viel Zeit in Anspruch nimmt. SSMS schneidet diese Ergebnismengen effizienter ab.
Tim Schmelter
1
Mögliches Duplikat von stackoverflow.com/questions/2248112/…
Aaron Bertrand

Antworten:

79

In der Vergangenheit ist ein ähnliches Problem aufgetreten, daher bin ich gespannt auf eine Lösung für diese Frage. Aaron Bertrands Kommentar zum OP führte zu einer Zeitüberschreitung bei Abfragen , wenn er über das Web ausgeführt wurde, aber superschnell, wenn er über SSMS ausgeführt wurde. Obwohl die Frage kein Duplikat ist, kann die Antwort durchaus auf Ihre Situation zutreffen.

Im Wesentlichen klingt es so, als hätte SQL Server möglicherweise einen beschädigten zwischengespeicherten Ausführungsplan. Sie treffen mit Ihrem Webserver auf den schlechten Plan, aber SSMS landet auf einem anderen Plan, da das ARITHABORT-Flag eine andere Einstellung aufweist (die sonst keine Auswirkungen auf Ihre bestimmte Abfrage / Ihren gespeicherten Prozess hätte).

Siehe ADO.NET, das die gespeicherte T-SQL-Prozedur aufruft, verursacht eine SqlTimeoutException für ein anderes Beispiel mit einer vollständigeren Erklärung und Auflösung.

StriplingWarrior
quelle
Dies sind einige interessante Hinweise, werden mehr darüber lesen und in ein paar Jahren zurückkommen. Danke.
iamserious
44
Hallo, habe den Cache geleert DBCC DROPCLEANBUFFERSund DBCC FREEPROCCACHEden Trick gemacht! Ich vermute, der Ausführungsplan war irgendwie beschädigt oder nicht aktualisiert. Ich bin zweifelhaft, dass es sich um eine dauerhafte Lösung handelt. Wenn sie einmal beschädigt werden könnte, könnte sie es erneut tun. Ich beschäftige mich also immer noch mit den plausiblen Ursachen. Da die Web-App wieder funktioniert, muss ich den Druck nicht spüren. Vielen Dank.
iamserious
2
@iamserious - du hast es geschafft, danke! Nachdem ich meine gespeicherte Prozedur geändert hatte, hatte ich das Timeout-Problem. Dann habe ich die beiden oben erwähnten DBCC-Befehle ausgeführt und das Problem behoben.
Induster
5
@Mangist: Da dies ein kompliziertes Problem ist, gibt es kein Patentrezept, aber Sie können versuchen, Hinweise zu den Abfragen zu verwenden OPTIMIZE FORoder OPTION(Recompile)abzufragen, die Probleme verursachen. Dieser Artikel beschreibt das Problem ausführlich. Wenn Entity Framework Ihre Abfragen generiert, bietet dieser Beitrag eine Möglichkeit, Ihren Abfragen einen Abfragehinweis hinzuzufügen.
StriplingWarrior
8
Anstatt DBCC DROPCLEANBUFFERS und DBCC FREEPROCCACHE auszuführen, die ALLE Ausführungspläne löschen, können Sie die gespeicherte Prozedur des Problems löschen und neu erstellen.
Jnoreiga
50

Ich habe auch festgestellt, dass Abfragen in SSMS langsam aus dem Web und schnell ausgeführt wurden, und ich fand schließlich heraus, dass das Problem ein sogenanntes Parameter-Sniffing war.

Die Lösung für mich bestand darin, alle im sproc verwendeten Parameter in lokale Variablen zu ändern.

z.B. Veränderung:

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

zu:

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

Scheint seltsam, aber es hat mein Problem behoben.

Zane
quelle
3
Wow, ich hatte das gleiche Problem und glaubte nicht, dass dies funktionieren würde, aber es tat es.
Lac Ho
1
Zane, weißt du, ob das Problem dadurch dauerhaft behoben wird oder ob es wieder auftreten kann? Danke dir!
Lac Ho
1
Seit dieser Änderung hatte ich keine Probleme mit der Geschwindigkeit der gespeicherten Prozedur.
Zane
1
Wow, es hat auch mein Problem behoben! Dies muss ein Fehler in SQL Server sein. Warum sollten SQL-Autoren gezwungen werden, durch diesen dummen Reifen zu springen, wenn MSSQL intern unter der Haube in lokale konvertieren könnte, um RIESIGE Leistungssteigerungen zu erzielen?
HerrimanCoder
3
Könnte es sein, dass durch das Ändern des Prozesses ein neuer Ausführungsplan erstellt wird, sodass die Lösung die Eingabevariable nicht wirklich in eine lokale Variable kopiert, sondern lediglich die Generierung eines neuen Ausführungsplans erzwingt (wie in der akzeptierten Lösung erläutert)?
Garland Pope
10

Nicht um Spam zu versenden, aber als hoffentlich hilfreiche Lösung für andere hat unser System ein hohes Maß an Zeitüberschreitungen festgestellt.

Ich habe versucht, die gespeicherte Prozedur so einzustellen, dass sie mithilfe von neu kompiliert wird. sp_recompileDadurch wurde das Problem für den einen SP behoben.

Letztendlich gab es eine größere Anzahl von SPs, bei denen eine Zeitüberschreitung auftrat , von denen viele dies noch nie zuvor getan hatten, DBCC DROPCLEANBUFFERSund DBBC FREEPROCCACHEdie Häufigkeit von Zeitüberschreitungen bei Vorfällen ist erheblich gesunken - es gibt immer noch vereinzelte Vorkommnisse, von denen ich vermute, dass die Planregeneration stattfindet eine Weile, und einige, in denen die SPs wirklich unterdurchschnittlich sind und eine Neubewertung benötigen.

Clint
quelle
1
Danke dafür. Das Markieren der Prozedur für die Neukompilierung mit dem Befehl sp_recompile hat bei mir funktioniert, als das DBCC DROPCLEANBUFFERSund DBCC FREEPROCCACHEkeinen Unterschied machte.
Steve
4

Könnte es sein, dass einige andere DB-Aufrufe, die vor dem Aufruf der Webanwendung durch den SP ausgeführt werden, eine Transaktion offen halten? Dies könnte ein Grund dafür sein, dass dieser SP wartet, wenn er von der Webanwendung aufgerufen wird. Ich sage, isolieren Sie den Aufruf in der Webanwendung (stellen Sie ihn auf eine neue Seite), um sicherzustellen, dass eine vorherige Aktion in der Webanwendung dieses Problem verursacht.

Tundey
quelle
1
Hallo @Tundey, ich habe den Anruf isoliert und es dauert immer noch ca. 30 Sekunden und es tritt eine Zeitüberschreitung auf. Also muss es etwas zwischen der Kommunikation sein, denke ich?
iamserious
1

Das einfache Neukompilieren der gespeicherten Prozedur (in meinem Fall die Tabellenfunktion) hat bei mir funktioniert

Guy Biber
quelle
1

Wie @Zane sagte, könnte es an Parameter-Sniffing liegen. Ich habe das gleiche Verhalten erlebt und mir den Ausführungsplan der Prozedur und alle Anweisungen des sp in einer Reihe angesehen (alle Anweisungen der Prozedur kopiert, die Parameter als Variablen deklariert und die gleichen Werte für die Variable als zugewiesen die Parameter hatten). Der Ausführungsplan sah jedoch völlig anders aus. Die Ausführung von sp dauerte 3-4 Sekunden und die Anweisungen in einer Reihe mit genau den gleichen Werten wurden sofort zurückgegeben.

Ausführungsplan

Nach einigem googeln fand ich eine interessante Lektüre über dieses Verhalten: Langsam in der Anwendung, Schnell in SSMS?

Beim Kompilieren der Prozedur weiß SQL Server nicht, dass sich der Wert von @fromdate ändert, kompiliert die Prozedur jedoch unter der Annahme, dass @fromdate den Wert NULL hat. Da alle Vergleiche mit NULL UNBEKANNT ergeben, kann die Abfrage überhaupt keine Zeilen zurückgeben, wenn @fromdate diesen Wert zur Laufzeit noch hat. Wenn SQL Server den Eingabewert als endgültige Wahrheit verwenden würde, könnte es einen Plan mit nur einem konstanten Scan erstellen, der überhaupt nicht auf die Tabelle zugreift (führen Sie die Abfrage SELECT * FROM Orders WHERE OrderDate> NULL aus, um ein Beispiel dafür zu sehen). . SQL Server muss jedoch einen Plan generieren, der das richtige Ergebnis zurückgibt, unabhängig davon, welchen Wert @fromdate zur Laufzeit hat. Andererseits besteht keine Verpflichtung, einen Plan zu erstellen, der für alle Werte am besten ist. Da davon ausgegangen wird, dass keine Zeilen zurückgegeben werden, entscheidet sich SQL Server für die Indexsuche.

Das Problem war, dass ich Parameter hatte, die null gelassen werden konnten, und wenn sie als null übergeben wurden, wurden sie mit einem Standardwert initialisiert.

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

Nachdem ich es geändert habe

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

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

es funktionierte wieder wie ein Zauber.

TomPez
quelle