Piano di esecuzione improvvisamente lento per Proc memorizzato


15

Sto cercando di capire un problema che stiamo riscontrando con SQL Server 2000. Siamo un sito Web moderatamente transazionale e abbiamo un proc memorizzato chiamato sp_GetCurrentTransactionsche accetta un ID cliente e due date.

Ora, a seconda delle date e del cliente, questa query può restituire qualsiasi valore compreso tra zero e migliaia di righe.

Il problema: quello che abbiamo riscontrato è che improvvisamente avremo una serie di errori (in genere Execution Timeout Expiredo simili) per un particolare client mentre provano a eseguire quel proc memorizzato. Quindi esaminiamo la query, la eseguiamo in SSMS e scopriamo che ci vogliono 30 secondi. Quindi ricompiliamo il proc memorizzato e -bang- ora viene eseguito in 300ms.

Ne ho parlato con il nostro DBA. Mi ha detto che il database ha creato un piano di query quando abbiamo creato il proc memorizzato. Ha detto che era un buon piano per quel set di parametri, ma se ci lanci un certo set di parametri, allora il piano non sarà il piano migliore per quei dati, e quindi lo vedrai funzionare lentamente.

Le opzioni che mi vengono presentate sono lo spostamento di quel problema da un proc memorizzato e di nuovo in SQL dinamico che ha il suo piano di esecuzione creato ad ogni corsa.

Mi sembra un passo indietro e sento che ci deve essere un modo per aggirare questo. Esiste un altro modo per affrontare questo problema?

Tutte le risposte sono apprezzate.


c'è un'istruzione if / else nel proc? Ho visto questo accadere quando il piano viene memorizzato nella cache dell'istruzione if e quindi tenta di essere eseguito sotto il blocco else usando il piano sbagliato. Questi errori corrispondono a un cambiamento nel proc?
Jeremy Gray,

@Jeremy: nessuna modifica al proc e nessun altro / if dichiarazioni.
Ciaran Archer,

Risposte:


14

Questo problema si chiama sniffing dei parametri.

Le versioni successive di SQL Server offrono più opzioni per gestirle come OPTION (RECOMPILE)o OPTIMIZE FORsuggerimenti.

Potresti provare a dichiarare le variabili nella procedura memorizzata, assegnando i valori dei parametri alle variabili e usando le variabili al posto dei parametri come sembra la maggior parte delle volte stai ottenendo un piano ragionevolmente soddisfacente.

Normalmente i piani più catastroficamente negativi sono quelli compilati per parametri con selettività molto elevata ma eseguiti con parametri con selettività bassa.

Supponendo che il piano generato sia più solido con questo approccio e soddisfacente per tutti i valori dei parametri, il vantaggio di questo approccio rispetto a quello suggerito da JNK è che non comporta un costo di compilazione per ogni chiamata.

Lo svantaggio è che per alcune esecuzioni il tempo di esecuzione potrebbe essere maggiore rispetto a un piano su misura per quei valori di parametro, quindi è un compromesso tra tempo di compilazione e tempo di esecuzione.


3
O "bind peeking" nella terminologia di Oracle
Gaius

Grazie @Gaius, buona conoscenza della terminologia per più di un RDBMS;)
Andrei Rînea

6

Invece di utilizzare SQL dinamico, puoi sempre modificare le tue chiamate proc in:

EXEC Database.dbo.usp_Myprocedure 'Parameter' WITH RECOMPILE

Le WITH RECOMPILEforze (hai indovinato!) Una ricompilazione del piano di esecuzione ogni volta che viene eseguito.

È inoltre possibile includere WITH RECOMPILEnella definizione del proc memorizzato:

CREATE PROCEDURE usp.MyProcedure (Parameters)
WITH RECOMPILE
AS
...

2

Potresti anche provare a decidere per il database quale piano utilizzare, anche se dovresti combattere un po 'con l'ottimizzatore, quindi è più fragile di quanto speri.

La tecnica è questa: suddividere la procedura memorizzata in 2, una per un set di parametri, una per un altro. Aggiungi le clausole where a ciascuna in modo tale che tra loro coprano tutti i possibili casi. Guarda i piani di query: uno dovrebbe essere ottimizzato per un set di parametri, l'altro per l'altro set. Potrebbe essere necessario armeggiare con la query per far sì che ciò accada, oppure potrebbe non essere possibile raggiungere la query, nel qual caso questo approccio non funzionerà.

Ora fai in modo che la tua procedura memorizzata originale controlli i valori dei parametri e invii a una delle due procedure memorizzate appropriate dal paragrafo precedente.

Questo può funzionare, ma è una specie di hack per forzare l'ottimizzatore a lavorare in modo più efficace per la tua query. Come tutti questi hack, nelle future versioni del database potrebbe non essere necessario o addirittura peggiorare le cose. Quindi, anche se funziona, devi decidere se ne vale la pena.



0

Hmmm ... se ci concentriamo solo su questa procedura memorizzata, sarei sorpreso che l'uso del piano di esecuzione memorizzato nella cache causerebbe il problema che stai vedendo. Vorrei chiedere di vedere il piano di esecuzione della procedura memorizzata utilizzando un set di parametri per il cliente e le due date. Mi chiedo se un indice più specifico sarebbe utile -> come su customerId, e solo le due date?


2
Perché la sorpresa? lo sniffing dei parametri è un problema abbastanza comune con questi sintomi e sembra che il DBA lo abbia identificato come il problema.
Martin Smith,

@MartinSmith - Sono sorpreso un po 'che il DBA che sia a conoscenza dello sniffing dei parametri non sappia dei suggerimenti per la ricompilazione ...
JNK,

@JNK - È vero. Non so perché non ne parlerebbero.
Martin Smith,

0

Le prestazioni improvvisamente degradanti sembrano un piano di query inefficiente che viene prodotto, probabilmente a causa della mancanza di statistiche. Esegui un profiler di SQL Server con le categorie di eventi "Errori e avvisi" impostate e verifica se sono presenti avvisi sulle statistiche mancanti.

Potrebbe anche mancare un indice o potrebbe essere necessario deframmentare gli indici poiché potrebbero essere troppo frammentati per essere utilizzati da SQL Server, facendo pensare che una scansione della tabella produrrà meno I / O.

@JNK solleva un grande punto sui proc memorizzati: questi vengono compilati in anticipo e il piano di query verrà archiviato insieme alla procedura memorizzata.

Non sono necessariamente d'accordo con l'utilizzo di WITH RECOMPILE poiché perdi quindi il vantaggio che il piano di query viene archiviato e riutilizzato. Ci sono alcuni casi in cui ciò è necessario, ad esempio se le statistiche di distribuzione nelle tabelle sottostanti differiscono notevolmente tra le chiamate, ma in genere, una volta che i dati nelle tabelle sono maturi, la distribuzione dei dati all'interno delle tabelle varierà minimamente.

Quindi, per riassumere:

  1. Controlla le statistiche mancanti
  2. Controlla la frammentazione dell'indice
  3. Crea e usa un proc memorizzato
  4. Rinominare proc - sp_ è uno spazio dei nomi di prefisso leggermente riservato per i proc interni di SQL Server del sistema - in modo che SQL Server cerchi sempre prima nel database master le procedure memorizzate. Rinominare proc usp_ invece di sp_ comporterà un aumento delle prestazioni, ma dubito che sia un tuo problema in questo caso.
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.