La stored procedure è lenta se chiamata dal Web, veloce da Management Studio


97

Ho una procedura memorizzata che scade follemente ogni volta che viene chiamata dall'applicazione web.

Ho avviato Sql Profiler e ho tracciato le chiamate in quel momento e finalmente ho scoperto queste cose:

  1. Quando vengono eseguite le istruzioni dall'interno di MS SQL Management Studio, con gli stessi argomenti (in effetti, ho copiato la chiamata alla procedura dalla traccia del profilo sql e l'ho eseguita): termina in 5 ~ 6 secondi in media.
  2. Ma quando viene chiamato dall'applicazione web, impiega più di 30 secondi (in traccia), quindi la mia pagina web va effettivamente in timeout per allora.

A parte il fatto che la mia applicazione web ha il proprio utente, ogni cosa è uguale (stesso database, connessione, server ecc.) Ho anche provato a eseguire la query direttamente in studio con l'utente dell'applicazione web e non ci vogliono più di 6 sec.

Come faccio a sapere cosa sta succedendo?

Presumo che non abbia nulla a che fare con il fatto che utilizziamo i livelli BLL> DAL o gli adattatori di tabella poiché la traccia mostra chiaramente il ritardo nella procedura effettiva. Questo è tutto quello a cui riesco a pensare.

MODIFICA Ho scoperto in questo collegamento che ADO.NET è impostato ARITHABORTsu true, il che è buono per la maggior parte del tempo, ma a volte questo accade e la soluzione suggerita è aggiungere with recompileun'opzione al processo memorizzato. Nel mio caso non funziona ma sospetto che sia qualcosa di molto simile a questo. Qualcuno sa cos'altro fa ADO.NET o dove posso trovare le specifiche?


1
Questo potrebbe essere correlato a quanti dati vengono restituiti?
Barry Kaye

@Barry: No, poiché eseguo la stessa procedura (copiata anche dalla traccia, ovvero gli stessi parametri) in Management Studio, viene eseguita entro 6 secondi.
iamserious

@Jayantha: Il punto NON è che sp è lento, ma QUALCOSA tra ado.net e sql lo è. Non vedo come la sp potrebbe fare la differenza.
iamserious

2
L'SP restituisce molti dati, ad esempio colonne image / text / varchar (max)? La quantità di dati da consumare sul client sarebbe enorme, il che richiederà molto tempo. SSMS interrompe questi risultati in una questione più efficiente.
Tim Schmelter

Risposte:


79

Ho avuto un problema simile in passato, quindi non vedo l'ora di vedere una soluzione a questa domanda. Il commento di Aaron Bertrand sull'OP ha portato a un timeout delle query quando eseguito dal web, ma super veloce quando eseguito da SSMS e, sebbene la domanda non sia un duplicato, la risposta potrebbe benissimo applicarsi alla tua situazione.

In sostanza, sembra che SQL Server possa avere un piano di esecuzione memorizzato nella cache danneggiato. Stai colpendo il piano sbagliato con il tuo server web, ma SSMS atterra su un piano diverso poiché c'è un'impostazione diversa sul flag ARITHABORT (che altrimenti non avrebbe alcun impatto sulla tua particolare query / procedura memorizzata).

Vedere ADO.NET che chiama T-SQL Stored procedure provoca un'eccezione SqlTimeoutException per un altro esempio, con una spiegazione e una risoluzione più complete.


Questi sono alcuni contatti interessanti, leggerò di più su di loro e torneremo tra poco .. grazie.
iamserious

44
Ciao, ho cancellato la cache DBCC DROPCLEANBUFFERSe DBCC FREEPROCCACHEho fatto il trucco! Immagino che il piano di esecuzione fosse in qualche modo danneggiato o non aggiornato. Dubito che sia una soluzione permanente, se potesse corrompersi una volta, potrebbe farlo di nuovo. Quindi sto ancora esaminando le cause plausibili. Poiché l'app Web è tornata a funzionare, non ho bisogno di sentire la pressione. Grazie mille
iamserious

2
@iamserious - l'hai inchiodato grazie! Dopo aver modificato la mia procedura memorizzata, stavo avendo il problema di timeout. Quindi ho eseguito i due comandi DBCC che hai menzionato sopra e ha risolto il problema.
Induster

5
@ Mangist: Poiché si tratta di una questione complicata, non esiste un proiettile d'argento, ma potresti provare a utilizzare OPTIMIZE FORo OPTION(Recompile)interrogare i suggerimenti sulle query che causano problemi. Questo articolo discute il problema in profondità. Se Entity Framework sta generando le tue query, questo post sembra fornire un modo per aggiungere un suggerimento per la query alle tue query.
StriplingWarrior

8
Invece di eseguire DBCC DROPCLEANBUFFERS e DBCC FREEPROCCACHE che cancelleranno TUTTI i piani di esecuzione, è possibile eliminare e ricreare la procedura memorizzata del problema.
jnoreiga

50

Ho anche riscontrato che le query venivano eseguite lentamente dal Web e velocemente in SSMS e alla fine ho scoperto che il problema era qualcosa chiamato analisi dei parametri.

La soluzione per me è stata quella di modificare tutti i parametri utilizzati in sproc in variabili locali.

per esempio. modificare:

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

per:

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

Sembra strano, ma ha risolto il mio problema.


3
Wow, ho avuto lo stesso problema e non credevo che avrebbe funzionato, ma ha funzionato.
Lac Ho

1
Zane, sai se questo risolverà definitivamente il problema o se può ripresentarsi? Grazie!
Lac Ho

1
Da quando ho apportato questa modifica non ho avuto problemi con la velocità della procedura memorizzata.
Zane

1
Wow, ha risolto anche il mio problema! Questo DEVE ESSERE un bug nel server sql. Perché costringere gli scrittori sql a saltare attraverso questo stupido cerchio quando MSSQL potrebbe convertirsi internamente in locale sotto il cofano, per ottenere enormi guadagni di prestazioni?
HerrimanCoder

3
Potrebbe essere che la modifica del proc provoca la creazione di un nuovo piano di esecuzione, quindi la soluzione non sta realmente copiando la variabile di input in una variabile locale, ma solo forzando la generazione di un nuovo piano di esecuzione (come spiegato nella soluzione accettata)?
Garland Pope

10

Non per lo spam, ma come soluzione auspicabilmente utile per gli altri, il nostro sistema ha riscontrato un alto grado di timeout.

Ho provato a impostare la procedura memorizzata da ricompilare utilizzando sp_recompilee questo ha risolto il problema per l'unico SP.

Alla fine c'era un numero maggiore di SP che erano scaduti, molti dei quali non l'avevano mai fatto prima, utilizzando DBCC DROPCLEANBUFFERSe DBBC FREEPROCCACHEil tasso di incidenti dei timeout è precipitato in modo significativo - ci sono ancora casi isolati, alcuni in cui sospetto che la rigenerazione del piano stia prendendo per un po ', e in alcuni casi in cui gli SP sono davvero poco efficienti e necessitano di una rivalutazione.


1
Grazie per questo. Contrassegnare la procedura per la ricompilazione utilizzando il comando sp_recompile ha funzionato per me quando DBCC DROPCLEANBUFFERSe DBCC FREEPROCCACHEnon ha fatto differenza.
Steve

4

Potrebbe essere che alcune altre chiamate DB effettuate prima che l'applicazione web chiami l'SP sta mantenendo una transazione aperta? Questo potrebbe essere un motivo per cui questo SP deve attendere quando viene chiamato dall'applicazione web. Dico isolare la chiamata nell'applicazione Web (inserirla in una nuova pagina) per garantire che qualche azione precedente nell'applicazione Web stia causando questo problema.


1
Ciao @ Tundey, ho isolato la chiamata e ci vogliono ancora circa 30 secondi e scade. quindi deve essere qualcosa tra la comunicazione, immagino?
iamserious

1

La semplice ricompilazione della stored procedure (funzione tabella nel mio caso) ha funzionato per me


1

come ha detto @Zane, potrebbe essere dovuto allo sniffing dei parametri. Ho sperimentato lo stesso comportamento e ho dato un'occhiata al piano di esecuzione della procedura ea tutte le istruzioni della sp di seguito (ho copiato tutte le istruzioni dalla procedura, dichiarato i parametri come variabili e assegnato gli stessi valori per la variabile come i parametri avevano). Tuttavia, il piano di esecuzione sembrava completamente diverso. L'esecuzione di sp ha richiesto 3-4 secondi e le istruzioni di seguito con gli stessi identici valori sono state immediatamente restituite.

progetto esecutivo

Dopo un po 'di ricerche su Google ho trovato una lettura interessante su quel comportamento: lento nell'applicazione, veloce in SSMS?

Durante la compilazione della procedura, SQL Server non sa che il valore di @fromdate cambia, ma compila la procedura presupponendo che @fromdate abbia il valore NULL. Poiché tutti i confronti con NULL restituiscono UNKNOWN, la query non può restituire alcuna riga, se @fromdate ha ancora questo valore in fase di esecuzione. Se SQL Server prendesse il valore di input come verità finale, potrebbe costruire un piano con solo una scansione costante che non accede affatto alla tabella (eseguire la query SELECT * FROM Orders WHERE OrderDate> NULL per vedere un esempio di questo) . Ma SQL Server deve generare un piano che restituisca il risultato corretto indipendentemente dal valore @fromdate in fase di esecuzione. D'altra parte, non vi è alcun obbligo di costruire un piano che sia il migliore per tutti i valori. Pertanto, poiché si presuppone che non venga restituita alcuna riga, SQL Server si accontenta di Index Seek.

Il problema era che avevo parametri che potevano essere lasciati nulli e se fossero passati come nulli sarebbero stati inizializzati con un valore predefinito.

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

Dopo averlo cambiato in

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

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

ha funzionato di nuovo come un incantesimo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.