SQL Server: copertura degli indici comprese tutte le colonne?


9

Il nostro team ha ereditato un'applicazione e un database associato. Gli sviluppatori precedenti sembrano aver imposto una regola in cui ogni indice, su ogni tabella, ha una clausola INCLUDE per aggiungere sempre ogni colonna che non fa altrimenti parte della chiave. Queste tabelle hanno in media da due a cinque indici o vincoli univoci oltre a chiavi esterne.

L'intento sembra essere quello di migliorare le prestazioni di SELECT indipendentemente da quale query viene lanciata nel database, poiché l'accesso avviene tramite un ORM che per impostazione predefinita (ma non sempre) recupera tutte le colonne. Ci aspettiamo che gli effetti collaterali di questo siano maggiori requisiti di archiviazione (possibilmente in modo significativo) e tempi di consegna aggiuntivi per INSERT / UPDATE / DELETE.

La domanda è: è una strategia sensata? Il nostro team ha una storia con SQL Server ma nessun membro si considera esperto del suo comportamento interno (anche se è stata sollevata la domanda che se questa strategia fosse ottimale, non sarebbe ormai il default?). Quali altri effetti collaterali (utilizzo di CPU / memoria / TempDB del server di database, ecc.) Dovremmo aspettarci o alcuni dei nostri presupposti sopra sono errati?

Inoltre, l'applicazione può essere installata sia in SQL Server on-premise (versioni dal 2012), sia in Azure SQL - dovremmo essere preparati per eventuali differenze tra i due o per ulteriori effetti collaterali su Azure, a seguito di ciò approccio?

Risposte:


8

L'ho fatto su indici specifici prima d'ora, per aiutare spesso a eseguire query pesanti. In effetti ciò che hanno fatto è creare più indici cluster: quando uno di questi indici viene utilizzato per trovare le righe non è necessario alcun lavoro aggiuntivo cercando il resto dei dati nell'indice cluster reale (o l'heap se non esiste un indice cluster reale) .

è una strategia sensata?

Per alcuni indici dove necessario per supportare determinati schemi di query, certamente sì.

Ma per fare questo con tutti gli indici, direi proprio di no.

Sarà uno spreco di spazio da fare dove non effettivamente necessario e rallenterà in modo significativo inserimenti / aggiornamenti. Potrebbe rallentare il numero di query di lettura che aiuta anche, poiché ogni pagina di indice contiene meno record, quindi qualsiasi query che deve fare riferimento a una parte dell'indice per il filtro ma che non utilizza tutte le altre colonne dovrà accedere a più pagine. Questo renderà il tuo database più affamato di memoria: quelle pagine dovranno essere caricate nel pool di buffer, espellendo potenzialmente altre pagine utili se la memoria è insufficiente. Se la compressione viene utilizzata su quegli indici per tentare di mitigare l'effetto sui requisiti di archiviazione e memoria, verrà invece applicato un carico aggiuntivo alle CPU.

poiché l'accesso avviene tramite un ORM che per impostazione predefinita (ma non sempre) recupera tutte le colonne

Questo è un modello comune con un utilizzo scarsamente ottimizzato di un ORM (o solo ORM ingenui) e in questi casi ho visto il consulente di indice di SQL Server (e strumenti di terze parti simili) suggerire indici con molte INCLUDEcolonne d, quindi sarei d'accordo con il tuo suggerimento che questo è il motivo per cui gli indici sono stati creati in questo modo.

Ma sebbene possa rendere tutte queste query leggermente più veloci e alcune significativamente più veloci, sospetto che in molti casi qualsiasi vantaggio sia così piccolo da non valere la memoria aggiuntiva richiesta dal tuo comune set di lavoro, lo spazio su disco e l'IO tra disco e memoria.

Ricorda inoltre che l'ORM potrebbe non selezionare tutte le colonne di tutte le tabelle toccate da una query, quindi il vantaggio potrebbe essere valido solo per la destinazione principale della richiesta corrente e gli indici più grandi potrebbero penalizzare la query quando vengono utilizzati altri oggetti per il filtro ma non restituire i dati ( SELECT * FROM table1 WHERE id IN (SELECT someID FROM table2 WHERE someColumn='DesiredValue')forse).

Un'altra considerazione per lo spazio in eccesso utilizzato, in particolare se i dati sono di grandi dimensioni, è che avrà un impatto sulla strategia di backup: costi di archiviazione e trasferimento per tali backup, tempi di ripristino potenziali e così via.

dovremmo essere preparati per eventuali differenze tra i due [on-prem e AzureSQL]

In generale, penso che le considerazioni qui saranno le stesse in ogni caso, sebbene l'eventuale costo in eccesso di memoria / I / O imposto dagli indici di grandi dimensioni potrebbe essere più direttamente visibile in Azure dove è possibile modificare il livello di servizio e quindi il costo dell'infrastruttura più facilmente anziché avere un set relativamente fisso di risorse hardware. Se si utilizzano livelli standard / premium invece di prezzi basati su vcore, il costo di I / O standard sarà influenzato maggiormente dal momento che il premio include significativamente più I / O per DTU. Se si usano backup o ridondanze multi-regione o altre funzionalità non locali in Azure, potrebbe esserci un costo di larghezza di banda associato allo spazio aggiuntivo occupato da indici non necessari.


Siamo andati avanti e abbiamo fatto questa rimozione. Un effetto collaterale è stato che su alcune tabelle, SELECTsenza specificare ha ORDER BYiniziato a restituire le stesse righe di prima ma con un ordine arbitrario diverso.
T2PS,

Questo non è inaspettato. L'ordine dei risultati senza "ORDINA PER" è per definizione non definito e può cambiare ogni volta che il pianificatore di query decide di adottare un approccio diverso, cosa che può fare a causa delle variazioni dell'indice o dei modelli di dati man mano che cresce. Altri fattori possono apportare tale modifica all'ordine in un secondo momento, anche senza questa modifica. Se si fa affidamento sull'ordinamento dell'output di un'istruzione, anche superficialmente, è necessario includere un 'ORDER BY' per garantirlo.
David Spillett,

Oh, sicuramente. Il commento precedente era inteso più come promemoria per chiunque trovasse questa risposta in seguito.
T2PS,

5

La domanda è: si tratta di una strategia ragionevole? .... (anche se è stata sollevata la domanda che se questa strategia fosse ottimale, non sarebbe ormai il default?)

Nella maggior parte dei casi questa non è una strategia ragionevole. Il motivo è che, in generale nei database OLTP, le righe restituite all'utente finale non saranno molto. (Generalizzazione)

La domanda che dovresti porti è, se stai cercando nelle colonne chiave, quante righe verranno restituite da quell'operazione di ricerca? E ripeterlo per le query che cercano su quella colonna.

Considera la tabella seguente, che restituisce molte colonne, where SelectiveIDField= ...

select columnA,columnC, ... columnZ
FROM dbo.BigTable
Where SelectiveIDField= '225122141';

Se la ricerca verrà restituita solo una riga selectiveIDField, la ricerca della chiave aggiuntiva è così negativa? (indovinando di avere indici cluster qui, altrimenti ricerca RID)

Farà solo una ricerca chiave aggiuntiva, un'esecuzione aggiuntiva + l'operatore join. Anche se fosse 10 o addirittura 100, sarebbe un impatto così grande? Ciò dipende anche da quanto viene eseguita la query e da quanto è importante il tempo di esecuzione.

Nel caso in cui sia trascurabile, basta creare l'indice SelectiveIDFielde chiamarlo un giorno, non dovrebbe valere i guadagni in lettura rispetto alle perdite in scrittura.

Quindi, in breve, a mio avviso, la creazione di indici sull'intera tabella non dovrebbe essere un approccio predefinito a meno che non si riscontri davvero un problema con una query e sia possibile migliorarlo drasticamente aggiungendo un intero indice di copertura.

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.