In che modo (e perché) TOP influisce su un piano di esecuzione?


35

Per una query moderatamente complessa che sto cercando di ottimizzare, ho notato che la rimozione della TOP nclausola modifica il piano di esecuzione. Avrei immaginato che quando una query includesse TOP nil motore di database avrebbe eseguito la query ignorando la TOPclausola, e poi alla fine avrebbe solo ridotto quel risultato impostato al n numero di righe richieste. Il piano di esecuzione grafico sembra indicare che questo è il caso - TOPè l'ultimo "passo". Ma sembra che stia succedendo di più.

La mia domanda è: in che modo (e perché) una clausola TOP n influisce sul piano di esecuzione di una query?

Ecco una versione semplificata di ciò che sta accadendo nel mio caso:

La query corrisponde a righe di due tabelle, A e B.

Senza la TOPclausola, l'ottimizzatore stima che ci saranno 19k righe dalla tabella A e 46k righe dalla tabella B. Il numero effettivo di righe restituite è 16k per A e 13k per B. Una corrispondenza hash viene utilizzata per unire questi due set di risultati per un totale di 69 righe (quindi viene applicato un ordinamento). Questa query avviene molto rapidamente.

Quando aggiungo TOP 1001l'ottimizzatore non usa una corrispondenza hash; invece prima ordina i risultati dalla tabella A (stessa stima / effettiva di 19k / 16k) e fa un ciclo nidificato rispetto alla tabella B. Il numero stimato di righe per la tabella B è ora 1, e la cosa strana è che TOP ninfluenza direttamente il numero stimato di esecuzioni (ricerca dell'indice) contro B: sembra essere sempre 2n + 1 , o nel mio caso 2003. Questa stima cambia di conseguenza se cambio TOP n. Naturalmente, poiché si tratta di un join nidificato, il numero effettivo di esecuzioni è 16k (il numero di righe dalla tabella A) e questo rallenta la query.

Lo scenario reale è un po 'più complesso ma questo cattura l'idea / comportamento di base. Entrambe le tabelle vengono cercate utilizzando le ricerche dell'indice. Questa è l'edizione Enterprise di SQL Server 2008 R2.


La query ha una ORDER BYclausola. Aggiungendo le TOPmodifiche in cui si verifica questo tipo di piano, ma sono più preoccupato di come influisce sul numero di esecuzioni di ricerche di indice rispetto alla tabella B ... (ovviamente i due potrebbero essere correlati - non lo so)
David,

1
Discussione correlata: il FAST num_rowssuggerimento per la query.
Remus Rusanu,

Risposte:


39

Avrei indovinato che quando una query include TOP n il motore di database eseguirà la query ignorando la clausola TOP, e quindi alla fine ridurrà quel risultato impostato al n numero di righe richieste. Il piano di esecuzione grafico sembra indicare che questo è il caso - TOP è l'ultimo "passo". Ma sembra che stia succedendo di più.

Il modo in cui quanto sopra è espresso mi fa pensare che potresti avere un'immagine mentale errata di come viene eseguita una query. Un operatore in un piano di query non è un passaggio (in cui l'intero set di risultati di un passaggio precedente viene valutato da quello successivo.

SQL Server utilizza un modello di esecuzione pipeline , in cui ciascun operatore espone metodi come Init () , GetRow () e Close () . Come suggerisce il nome GetRow () , un operatore produce una riga alla volta su richiesta (come richiesto dal suo operatore padre). Ciò è documentato nel riferimento Operatori logici e fisici della documentazione online , con maggiori dettagli nel mio post sul blog Perché i piani di query vengono eseguiti al contrario . Questo modello di fila alla volta è essenziale per formare un'intuizione sonora per l'esecuzione della query.

La mia domanda è: in che modo (e perché) una TOPclausola n influisce sul piano di esecuzione di una query?

Alcune operazioni logiche come i TOPsemi join e il FAST n suggerimento per le query influiscono sul modo in cui l'ottimizzatore delle query costa alternative al piano di esecuzione. L'idea di base è che una possibile forma del piano potrebbe restituire le prime n righe più rapidamente di un piano diverso ottimizzato per restituire tutte le righe.

Ad esempio, l'unione di cicli nidificati indicizzati è spesso il modo più rapido per restituire un piccolo numero di righe, sebbene hash o unione unita con scansioni possano essere più efficienti su set più grandi. Il modo in cui l'ottimizzatore delle query ragiona su queste scelte è impostando un obiettivo di riga in un determinato punto dell'albero logico delle operazioni.

Un obiettivo di riga modifica il costo delle alternative del piano di query. L'essenza di ciò è che l'ottimizzatore inizia costando a ciascun operatore come se fosse richiesto l'intero set di risultati, imposta un obiettivo di riga nel punto appropriato e quindi ripercorre l'albero del piano stimando il numero di righe che si aspetta di esaminare per raggiungere l'obiettivo della fila.

Ad esempio, una logica TOP(10)imposta un obiettivo di riga pari a 10 in un determinato punto dell'albero della query logica. I costi degli operatori che portano all'obiettivo di riga vengono modificati per stimare quante righe devono produrre per raggiungere l'obiettivo di riga. Questo calcolo può diventare complesso, quindi è più facile comprenderlo con un esempio completamente elaborato e piani di esecuzione con annotazioni. Gli obiettivi di riga possono influenzare più della scelta del tipo di join o se le ricerche e le ricerche sono preferite alle scansioni. Maggiori dettagli su questo qui .

Come sempre, un piano di esecuzione selezionato sulla base di un obiettivo di riga è soggetto alle capacità di ragionamento dell'ottimizzatore e alla qualità delle informazioni fornite. Non tutti i piani con un obiettivo di riga produrranno il numero richiesto di righe più velocemente nella pratica, ma in base al modello di determinazione dei costi lo farà.

Laddove un piano obiettivo di riga non si rivela più veloce, in genere esistono modi per modificare la query o fornire informazioni migliori all'ottimizzatore in modo che il piano selezionato naturalmente sia il migliore. L'opzione appropriata nel tuo caso dipende ovviamente dai dettagli. La funzionalità dell'obiettivo di riga è generalmente molto efficace (sebbene sia presente un bug a cui prestare attenzione quando viene utilizzato nei piani di esecuzione paralleli).

La tua query e il tuo piano particolari potrebbero non essere adatti per un'analisi dettagliata qui (fornisci comunque un piano di esecuzione reale se lo desideri), ma spero che le idee delineate qui ti consentano di fare progressi.


12

Quando si utilizza TOP, l'ottimizzatore vede l'opportunità di fare meno lavoro. Se chiedi 10 righe, allora c'è una buona probabilità che non sia necessario consumare l'intero set. Quindi l'operatore TOP può essere spinto molto più a destra. Continuerà a richiedere le righe dall'operatore successivo (alla sua destra), fino a quando non avrà ricevuto abbastanza.

Fai notare che senza TOP, la query ordina i dati alla fine. Se il motore fosse in grado di sapere quante file sarebbero state soddisfatte in anticipo dal join, potrebbe anche scegliere di utilizzare un piano simile, posizionando la parte superiore a sinistra. Ma con lo sforzo di fare un Hash Match relativamente alto, e presumibilmente nessuna opzione per un Merge Join, l'ottimizzatore potrebbe preferire filtrare il TOP più a destra.

Quando viene eseguita una query nella tabella B, viene recuperata una singola riga alla volta. Ecco perché la stima è 1. Presuppone anche che troverà quella riga solo il 50% delle volte. Quindi suppone che avrà bisogno di 2n + 1 cerca di trovarlo.


Non sembra giusto che il numero stimato di righe cambierebbe in base al modo in cui i dati vengono recuperati. Il modo in cui ottiene i dati non dovrebbe influire sulla cardinalità. Un cambiamento nel modo in cui recupera si rifletterebbe invece nel numero di esecuzioni, giusto?
David,

Il "Numero stimato di righe" è per esecuzione. In un ciclo annidato, è molto probabile che venga eseguito più di una volta.
Rob Farley,

Si tratterebbe quindi di un comportamento diverso rispetto al numero effettivo di righe e al numero (effettivo) di esecuzioni. Se il piano reale mostra 16.834 esecuzioni effettive e 15.407 righe effettive restituite, prendo questo per dire che ha fatto 16k ricerche ma ha trovato solo 15k corrispondenti al predicato. Se significasse 15k righe per esecuzione, questo sarebbe 15k * 16k = 240 milioni di righe - circa 10 volte più grande della tabella ...
David

Inoltre, non sono sicuro di seguire l'ultima affermazione della tua risposta. Quando dici 2n + 1 cerca di trovare "it", cosa intendi con "it"? Sicuramente non una sola riga? Vuoi dire che l'ottimizzatore presume che per ogni data riga in A ci sia una probabilità del 50% che sarà abbinata a B e quindi dovrà "provare" 2003 righe da A per ottenere 1001 partite da B? Questo comportamento è documentato ovunque da Microsoft? E cosa c'entra la TOPclausola? Grazie per la tua risposta (s) / pazienza.
David,

Sì, le righe stimate sono per esecuzione. Le righe effettive sono totali. Tuttavia, non vi è alcun problema con un operatore che restituisce più righe di quelle presenti nella tabella, poiché è molto semplice dimostrare agli operatori che restituiscono la stessa riga più volte.
Rob Farley,
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.