La nuova proprietà "Righe effettive lette" nei piani di esecuzione (che in SQL Server Management Studio viene visualizzata come "Numero di righe lette") è stata un'aggiunta gradita ai sintonizzatori delle prestazioni. È come avere un nuovo superpotere, essere in grado di raccontare il significato di Seek Predicate v the Residual Predicate all'interno di un operatore Seek. Mi piace questo, perché può essere davvero significativo per interrogare.
Diamo un'occhiata a due query, che sto eseguendo su AdventureWorks2012. Sono molto semplici:uno elenca le persone chiamate John S e l'altro elenca le persone chiamate J Smith. Come tutte le buone rubriche, abbiamo un indice su Cognome, Nome.
select FirstName, LastName from Person.Person where LastName like 'S%' and FirstName = 'John'; select FirstName, LastName from Person.Person where LastName = 'Smith' and FirstName like 'J%';
Se sei curioso, ottengo 2 righe indietro dalla prima e 14 righe dalla seconda. In realtà non sono così interessato ai risultati, sono interessato ai piani di esecuzione.
Vediamo cosa sta succedendo. Ho aperto una copia precedente di SQL Sentry Plan Explorer e ho aperto i miei piani fianco a fianco. Per inciso, avevo eseguito entrambe le query insieme e quindi entrambi i piani erano nello stesso file .sqlplan. Ma potrei aprire lo stesso file due volte in PE e metterli felicemente fianco a fianco in gruppi di schede.
Grande. Sembrano uguali! Vedo che Seek a sinistra sta producendo due righe invece di quattordici – ovviamente questa è la query migliore.
Ma con una finestra più grande, avrei visto più informazioni ed è una fortuna aver eseguito le due query nello stesso batch.
Puoi vedere che la seconda query, che ha prodotto 14 righe anziché 2 righe, avrebbe richiesto oltre l'80% del costo! Se eseguissi le query separatamente, ognuna mi mostrerebbe il 100%.
Ora confrontiamo con l'ultima versione di Plan Explorer.
La cosa che mi salta subito all'occhio è l'avvertimento. Diamo un'occhiata un po' più da vicino.
L'avviso dice "L'operazione ha causato IO residuo. Il numero effettivo di righe lette era 2.130, ma il numero di righe restituite era 2". Abbastanza sicuro, più in alto vediamo "Righe effettive lette" che dice 2.130 e Righe effettive a 2.
Whoa! Per trovare quelle righe, abbiamo dovuto esaminare 2.130?
Vedete, il modo in cui funziona il Seek è iniziare pensando al Seek Predicate. Questo è quello che sfrutta bene l'indice e che in realtà fa sì che l'operazione sia una ricerca. Senza un predicato di ricerca, l'operazione diventa una scansione. Ora, se questo predicato di ricerca è garantito per essere al massimo su una riga (come quando ha un operatore di uguaglianza su un indice univoco), allora abbiamo una ricerca Singleton. Altrimenti, abbiamo una scansione dell'intervallo e questo intervallo può avere un prefisso, un inizio e una fine (ma non necessariamente sia un inizio che una fine). Questo definisce le righe della tabella che ci interessano per la ricerca.
Ma "interessato a" non significa necessariamente "restituito", perché potremmo avere più lavoro da fare. Quel lavoro è descritto nell'altro predicato, che è spesso noto come predicato residuo.
Ora che Residual Predicate potrebbe effettivamente svolgere la maggior parte del lavoro. Certamente è qui:sta filtrando le cose da 2.130 righe a solo 2.
La scansione dell'intervallo inizia nell'indice a "John S". Sappiamo che se c'è un "John S", questa deve essere la prima fila che può soddisfare il tutto. "Ian S" non può. Quindi possiamo cercare nell'indice a quel punto per avviare il nostro Range Scan. Se osserviamo il Plan XML, possiamo vederlo esplicitamente.
Nota che non abbiamo un prefisso. Ciò si applica quando hai un'uguaglianza nella prima colonna all'interno dell'indice. Abbiamo solo StartRange ed EndRange. L'inizio dell'intervallo è "Maggiore o uguale" (GE) ScanType, al valore "S, Giovanni" (i riferimenti delle colonne fuori schermo sono Cognome, Nome) e la fine dell'intervallo è "Inferiore a" ( LT) il valore T. Quando la scansione raggiunge T, è fatta. Niente più da fare. The Seek ha ora completato la sua scansione del raggio. E in questo caso, restituisce 2.130 righe!
Tranne che in realtà non restituisce 2.130 righe, legge solo 2.130 righe. Vengono letti nomi come Barry Sai e Ken Sánchez, ma vengono restituiti solo i nomi che soddisfano il controllo successivo:il predicato residuo che assicura che il nome sia John.
La voce Actual Rows Read nelle proprietà dell'operatore Index Seek ci mostra questo valore di 2.130. E sebbene sia visibile nelle versioni precedenti di Plan Explorer, non riceviamo alcun avviso al riguardo. È relativamente nuovo.
La nostra seconda domanda (cercando J Smith) è molto più interessante e c'è un motivo per cui si stima che sia più di 4 volte più economica.
Qui conosciamo esattamente il cognome (Smith) e la scansione dell'intervallo è sul nome (J%).
È qui che entra in gioco il prefisso.
Vediamo che il nostro prefisso è un operatore di uguaglianza (=, ScanType="EQ") e che LastName deve essere Smith. Non abbiamo ancora considerato l'inizio o la fine dell'intervallo, ma il prefisso ci dice che l'intervallo è incluso nella parte dell'indice in cui LastName è Smith. Ora possiamo trovare le righe>=J e
C'è ancora un predicato residuo qui, ma questo serve solo per assicurarsi che "LIKE J%" sia effettivamente testato. Sebbene ci sembri intuitivo che "LIKE J%" sia esattamente equivalente a ">=J e
Prima del Service Pack 3 di SQL Server 2012, non avevamo questa proprietà e per avere un'idea della differenza tra le righe effettive lette e le righe effettive, avremmo dovuto usare il flag di traccia 9130. Ecco questi due piani con quella TF attivata:
Puoi vedere che questa volta non ci sono avvisi, perché l'operatore Seek restituisce tutte le 2130 righe. Penso che se stai usando una versione di SQL Server che supporta questa lettura di righe effettive, dovresti smettere di usare il flag di traccia 9130 nelle tue indagini e iniziare invece a guardare gli avvisi in Plan Explorer. Ma soprattutto, capisci cosa fanno i tuoi operatori, perché così sarai in grado di interpretare se sei soddisfatto del piano o se devi agire.
In un altro post, ti mostrerò una situazione in cui potresti preferire che le righe effettive lette siano superiori alle righe effettive.
@rob_farley