Ricerca indice e scansione indice


64

Guardando un piano di esecuzione di una query a esecuzione lenta, ho notato che alcuni dei nodi sono indicizzati e alcuni di essi sono indicizzati.

Qual è la differenza tra e cerca indice e una scansione dell'indice?

Quale si comporta meglio?

In che modo SQL sceglie l'uno rispetto all'altro?

Mi rendo conto che si tratta di 3 domande, ma penso che rispondere alla prima spiegherà le altre.


6
Hai un bel riferimento su use-the-index-luke .
Marian,

7
Non tutte le scansioni sono errate, a volte è il modo più efficiente per soddisfare la query. Si noti inoltre che non tutte le ricerche sono ricerche, spesso in realtà sono scansioni dell'intervallo e la ricerca indica solo come è arrivato all'inizio dell'intervallo.
Aaron Bertrand

@AaronBertrand ma se arriva all'inizio dell'intervallo e lo legge, fondamentalmente significa che hai bisogno dei dati comunque. Inoltre, cerca la fine dell'intervallo.
George Polevoy,

Risposte:


76

Versione breve: cercare è molto meglio

Versione meno breve: la ricerca è generalmente molto migliore, ma molte ricerche (causate ad esempio da una cattiva progettazione di query con cattive sottoquery correlate o perché stai facendo molte query in un'operazione del cursore o in un altro ciclo) possono essere peggiori di un scansione, specialmente se la tua query potrebbe finire per restituire dati dalla maggior parte delle righe nella tabella interessata.

Aiuta a coprire l'intera famiglia per le operazioni di ricerca dei dati per comprendere appieno le implicazioni delle prestazioni.

Scansioni delle tabelle: senza indici rilevanti per la query, il planner è costretto a utilizzare una scansione delle tabelle, il che significa che ogni riga viene esaminata. Ciò può comportare la lettura dal disco di ogni pagina relativa ai dati della tabella, che è spesso il caso peggiore. Si noti che per alcune query verrà utilizzata una scansione della tabella anche quando è presente un indice utile; ciò di solito perché i dati nella tabella sono così piccoli che è più fastidioso attraversare gli indici (in questo caso ci si aspetterebbe che pianificare di cambiare con l'aumentare dei dati, presupponendo che la misura della selettività dell'indice sia buona).

Scansioni dell'indice con ricerche di righe: senza l'indice che può essere utilizzato direttamente per una ricerca, ma è presente un indice contenente le colonne giuste, è possibile utilizzare una scansione dell'indice. Ad esempio, se si dispone di una tabella di grandi dimensioni con 20 colonne con un indice su column1, col2, col3 e si genera SELECT col4 FROM exampletable WHERE col2=616, in questo caso scansionare l'indice su query col2è meglio che scansionare l'intera tabella. Una volta trovate le righe corrispondenti, le pagine dei dati devono essere lette per prelevare col4 per l'output (o ulteriore unione), che è la fase di "ricerca dei segnalibri" quando la si vede nei piani di query.

Scansioni dell'indice senza ricerche diSELECT col1, col2, col3 FROM exampletable WHERE col2=616 righe : se l'esempio precedente era lo sforzo extra per leggere le pagine di dati non è necessario: una volta col2=616trovata la corrispondenza delle righe di indice, tutti i dati richiesti sono noti. Questo è il motivo per cui a volte vedi colonne che non verranno mai cercate, ma che probabilmente verranno richieste per l'output, aggiunte alla fine degli indici: può salvare le ricerche di righe. Quando si aggiungono colonne a un indice solo per questo motivo e per questo motivo, aggiungerle con la INCLUDEclausola per indicare al motore che non è necessario ottimizzare il layout dell'indice per le query basate su queste colonne (questo può accelerare gli aggiornamenti effettuati su quelle colonne) . Le scansioni dell'indice possono derivare anche da query senza clausole di filtro: SELECT col2 FROM exampletableeseguiranno la scansione di questo indice di esempio anziché delle pagine della tabella.

Ricerche indice (con o senza ricerche di righe) : in una ricerca non viene considerato tutto l'indice. Per la query, SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567il motore di query può trovare la prima riga che corrisponde eseguendo una ricerca basata sull'albero sull'indice, c1quindi può navigare nell'indice fino a quando non arriva alla fine dell'intervallo (questo è lo stesso con una query per c1=1234quanto vi potrebbero essere molte righe corrispondono alla condizione anche per un =funzionamento). Ciò significa che devono essere lette solo le pagine dell'indice pertinenti (più alcune necessarie per la ricerca iniziale) anziché ogni pagina dell'indice (o tabella).

Indici cluster: con un indice cluster i dati della tabella vengono archiviati nei nodi foglia di quell'indice anziché in una struttura heap separata. Ciò significa che non sarà mai necessario effettuare ulteriori ricerche di riga dopo aver trovato le righe utilizzando tale indice, indipendentemente dalle colonne necessarie [a meno che non si disponga di dati fuori pagina come TEXTcolonne o VARCHAR(MAX)colonne contenenti dati lunghi].

Per questo motivo puoi avere un solo indice cluster [1] , l'indice cluster è la tua tabella invece di avere una struttura heap separata, quindi se usi uno [2] scegli dove metterlo con cura per ottenere il massimo guadagno.

Si noti inoltre che l'indice cluster perché la "chiave di clustering" per la tabella ed è incluso in ogni indice non cluster sulla tabella, quindi un indice cluster ampio non è generalmente una buona idea.

[1] In realtà, si può effettivamente avere più indici cluster attraverso la definizione di indici non cluster che coprono o che contengono ogni colonna sul tavolo, ma questo è probabile che sia uno spreco di spazio ha un impatto sulle prestazioni in scrittura, quindi se si considera farlo assicurarsi hai davvero bisogno.

[2] Quando dico "se si utilizza un indice cluster", faccio notare che è generalmente consiglia di fare avere uno su ogni tavolo. Ci sono eccezioni come per tutte le regole empiriche, le tabelle che vedono poco altro che inserimenti di massa e letture non ordinate (forse tabelle di stadiazione per i processi ETL) sono il contro esempio più comune.

Punto aggiuntivo: scansioni incomplete:

È importante ricordare che, a seconda del resto della query, una scansione di tabella / indice potrebbe non eseguire la scansione dell'intera tabella, se la logica consente al piano di query di essere in grado di interromperlo anticipatamente. L'esempio più semplice di questo è SELECT TOP(1) * FROM HugeTable: se guardi il piano di query per questo vedrai che una sola riga è stata restituita dalla scansione e se guardi le statistiche IO ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable) vedrai che legge solo un numero molto piccolo di pagine (forse solo una).

Lo stesso può accadere se il predicato di una clausola WHEREo JOIN ... ONpuò essere eseguito contemporaneamente alla scansione che è l'origine se i suoi dati. Il planner / runner delle query a volte può essere molto intelligente nel reindirizzare i predicati verso le origini dati per consentire la terminazione anticipata delle scansioni in questo modo (e talvolta si può essere intelligenti nel riorganizzare le query per aiutarlo a farlo!). Mentre i dati scorrono da destra a sinistra secondo le frecce nella visualizzazione del piano di query standard, la logica viene eseguita da sinistra a destra e ogni passaggio (da destra a sinistra) non viene necessariamente eseguito fino al completamento prima che possa iniziare il successivo. Nel semplice esempio sopra riportato, se si considera ciascun blocco nel piano di query come agente, l' SELECTagente richiede TOPall'agente una riga che a sua volta chiede alTABLE SCANagente per uno, quindi l' SELECTagente ne chiede un altro, ma l' TOPagente sa che non è necessario non si preoccupa nemmeno di chiedere al lettore di tabelle, l' SELECTagente riceve una risposta "non più pertinente" e sa tutto il lavoro svolto. Molte operazioni bloccare questo tipo di ottimizzazione, naturalmente, così spesso negli esempi più complessi di una scansione di tabella / indice in realtà non leggere ogni riga, ma fate attenzione a non saltare alla conclusione che ogni scansione deve essere un'operazione costosa.


6

In generale, le ricerche sono buone, le scansioni sono cattive.

Le ricerche sono dove la query è in grado di utilizzare in modo efficace l'indice e utilizzarlo per trovare le righe necessarie.

Le scansioni sono dove la query sta esaminando l'intero indice cercando di trovare ciò di cui ha bisogno.

Come sceglie SQL? In profondità all'interno di Query Optimizer, la decisione viene presa in base alla query e agli indici disponibili e alle informazioni statistiche associate a tali indici.

Ci sono alcuni libri da leggere che potrebbero essere di interesse qui - Entrambi dalla libreria Red-Gate su http://www.red-gate.com/community/books/

  • Piani di esecuzione di SQL Server di Grant Fritchey
  • Inside the Query Optimizer di Benjamin Nevarez
  • Statistiche di SQL Server di Holger Schmeling

7
Per lo stesso piano una scansione di una sola tabella è buona, un milione di ricerche è cattiva. Quindi la tua prima affermazione non è del tutto corretta.
Marian,

In effetti, la ricerca dell'indice e la scansione dell'indice hanno ciascuno il suo uso, non si può dire che uno sia migliore di un altro SENZA il contesto delle tabelle e delle query sottostanti. Il più delle volte, se una tabella presenta statistiche inesatte, il piano di esecuzione può risultare non ottimale, ad esempio una ricerca dell'indice viene erroneamente scelta rispetto a una scansione dell'indice e viceversa.
jyao,

5

Se desideri approfondire l'argomento, un libro molto utile (almeno per me) sono i piani di esecuzione di SQL Server di Grant Fritchey, disponibili gratuitamente su RedGate qui .

Se hai una domanda come

SELECT *
FROM myTable

Probabilmente SQL Server utilizzerà una scansione dell'indice, poiché deve passare attraverso tutte le righe per visualizzare i risultati richiesti.

Anzi,

SELECT *
FROM myTable
WHERE myID = 1

si tradurrà sicuramente in una ricerca dell'Indice. SQL Server utilizzerà la struttura B-tree dell'indice myID e il recupero della riga corretta sarà molto più veloce.


Non so se concordo con "certamente" - anche se un indice ha myID come colonna principale, una ricerca potrebbe non essere la risposta ottimale (dipende da molte cose, come se sia unica - che potrebbe essere vero nella tabella clienti ma non per ID cliente nella tabella degli ordini, quante colonne devono essere coperte ma non nell'indice, ecc.).
Aaron Bertrand

Non credo che questa risposta copra davvero le domande poste.
Zero3,

5

Altri hanno definito abbastanza bene le differenze tra ricerca e scansione. In questo caso, la query stessa e il planner di esecuzione dovrebbero fornire le informazioni necessarie per vedere quali valori vengono utilizzati come predicati (filtri) per la query in ciascuna parte. In genere è una buona pratica aggiungere sempre indici non cluster su chiavi esterne e, a seconda dei casi d'uso nel codice del programma, è possibile che si desideri esaminare la creazione di ulteriori indici a più colonne o di indici di colonna inclusi. Con la terminologia qui presentata, una ricerca su Google darà risultati decenti su esempi su ciascuno.

Ad esempio, supponiamo che il tuo codice stia eseguendo una query per la colonna A e la colonna B su determinati filtri, ma desideri anche restituire i valori della colonna C e della colonna E, potresti voler creare un indice sulla colonna A e B con INCLUDE opzione contenente Colonna C ed E. In questo modo una singola ricerca dell'indice restituirà tutto ciò di cui hai bisogno, poiché non è necessario eseguire una ricerca per recuperare gli altri valori (C ed E) sulla stessa riga.

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.