Introduzione
La documentazione del prodotto SQL Server è un po' leggera sull'argomento degli obiettivi di riga . I principali riferimenti ufficiali sono in:
- Suggerimenti (Transact-SQL) – Query (
FAST
eDISABLE_OPTIMIZER_ROWGOAL
suggerimenti) - DBCC TRACEON – Flag di traccia (Transact-SQL) (flag di traccia 4138)
- L'esecuzione di una query potrebbe richiedere molto tempo se Query Optimizer utilizza l'operatore Top (KB 2667211)
Quando le persone chiedono più informazioni di quelle ivi contenute, normalmente rimango loro una o più delle seguenti:
- Obiettivi di riga in azione dal team di ottimizzazione delle query di SQL Server
- Obiettivi di riga rivisitati:indicazioni per l'hint FAST anche da parte del team di ottimizzazione delle query di SQL ServerSQL ServerSQL Server Query Optimization Team
- Row Goals Gone Rogue di Bart Duncan
- Dentro l'ottimizzatore:riga obiettivi in profondità da me
- Il consiglio per l'ottimizzazione SSIS che manca a tutti di Rob Farley
Per riassumere brevemente:la funzione obiettivo di riga consente all'ottimizzatore di generare un piano di esecuzione (o parti di un piano di esecuzione) con l'obiettivo di restituire rapidamente un certo numero di righe. Ciò è in contrasto con il comportamento normale (senza un obiettivo di fila), che mira a trovare un piano ottimizzato per l'intero potenziale set di risultati.
Una strategia di obiettivo di riga generalmente significa favorire le operazioni di navigazione non bloccanti (ad esempio, join di cicli nidificati, ricerche di indici e ricerche) rispetto a operazioni di blocco basate su set come l'ordinamento e l'hashing. Questo può essere utile ogni volta che il cliente può trarre vantaggio da un avvio rapido e da un flusso costante di righe (con forse un tempo di esecuzione complessivo più lungo – vedere il post di Rob Farley sopra). Ci sono anche gli usi più ovvi e tradizionali, ad es. nella presentazione dei risultati una pagina alla volta.
Naturalmente, c'è un elemento di rischio legato a un piano di obiettivi di fila. Se tutto va a buon fine come previsto dall'ottimizzatore (date le informazioni disponibili e le ipotesi di modellazione fatte), il piano di esecuzione inizierà a trasmettere in streaming il numero di righe richiesto in modo più rapido ed efficiente di quanto sarebbe stato senza l'obiettivo di riga.
Sfortunatamente, quando la strategia del goal di fila va storta, può essere un disastro per le prestazioni (vedi il post di Bart Duncan). Ciò può accadere, ad esempio, quando l'ottimizzatore ha informazioni incomplete, incontra una distribuzione dei dati sfavorevole o fa un'ipotesi non sicura. In ogni caso, la causa delle scarse prestazioni è quasi sempre la necessità di elaborare molte più righe in fase di esecuzione rispetto a quelle previste dall'ottimizzatore.
Può essere molto utile identificare le aree del piano di esecuzione interessate da un obiettivo di fila, perché ci aiuta a capire perché l'ottimizzatore ha fatto le scelte che ha fatto. Ciò è particolarmente importante quando la logica dell'obiettivo di fila produce un risultato negativo. Senza comprendere il ruolo svolto dall'obiettivo di riga, potrebbe sembrare che l'ottimizzatore abbia semplicemente sottovalutato il numero di righe, portando le persone a cercare nei posti sbagliati (ad esempio le statistiche) per una causa principale.
Impostazione degli obiettivi di riga
È molto più facile cercare gli effetti del goal di fila se si sa che tipo di cose potrebbero causare l'impostazione di un goal di fila in primo luogo. La documentazione ufficiale parla spesso di obiettivi di riga associati alle parole chiave TOP
, FAST
, IN
e EXISTS
. Questo può lasciare il lettore con una comprensione incompleta o fuorviante, quindi vale la pena prendersi un momento per chiarire alcuni aspetti.
Voglio sottolineare fin da subito che uso di parole chiave T-SQL specifiche in una query non garantisce che verrà impostato un obiettivo di riga . La documentazione ufficiale menziona alcune parole chiave per aiutare le persone a identificare scenari comuni in cui gli obiettivi di riga possono essere introdotto, senza entrare in troppi tecnicismi.
Un secondo punto generale da tenere a mente è che un obiettivo di fila viene impostato solo quando l'obiettivo sarebbe inferiore alla stima normale . Dopotutto, non ha molto senso generare un frammento di piano ottimizzato per 100 righe se si prevede che l'intera operazione produca comunque solo 50 righe. Per essere più chiari, questo punto si applica sempre a tutti i modi in cui è possibile impostare un goal di fila. Se ti aspetti un goal di fila, ma non ne vedi uno, questa è una probabile causa.
Infine, per il preambolo, si noti che gli obiettivi di riga sono una cosa di ottimizzazione basata sui costi; un obiettivo di riga influisce sulle scelte dell'ottimizzatore, quindi se non ci sono scelte da fare (ad esempio un piano banale) non c'è alcun effetto obiettivo di riga.
Diamo ora un'occhiata alle cose che possono impostare un obiettivo di fila:
VELOCE e TOP
Usando il FAST
il suggerimento per la query è un modo affidabile per impostare un obiettivo di riga nella radice del piano di esecuzione (fatte salve le eccezioni generali sopra indicate). A SET ROWCOUNT n
imposta anche un obiettivo di riga di livello superiore simile (quando n
non è zero ovviamente) per le affermazioni a cui si applica.
Scrivere un TOP
Anche la clausola in una query risulta molto spesso in un obiettivo di riga. Finché il piano di esecuzione finito prevede un operatore Top fisico, è probabile che almeno una parte del piano al di sotto dell'operatore Top sia stata interessata da un obiettivo di riga (di nuovo, si applicano i termini e le condizioni generali).
Tieni presente che gli operatori principali introdotti da Query Optimizer (senza un TOP
specificato dalla query clausola) può anche impostare un goal di fila. Questo è importante, perché ci sono tutti i modi in cui ciò può accadere, ad esempio quando si filtra su un semplice numero di riga, come mostrato nella seguente query AdventureWorks:
SELECT THN.RowNum, THN.TransactionID FROM ( SELECT TH.TransactionID, RowNum = ROW_NUMBER() OVER ( ORDER BY TH.TransactionID ASC) FROM Production.TransactionHistory AS TH WHERE TH.ProductID = 400 ) AS THN WHERE THN.RowNum >= 10 AND THN.RowNum < 20 ORDER BY THN.RowNum ASC;
Il piano di esecuzione per quella query include un operatore Top aggiunto dall'ottimizzatore (per limitare il numero di righe elaborate a 20):