Principali considerazioni
Vedo un vantaggio importante per gli heap e uno per le tabelle raggruppate, oltre a una terza considerazione che può andare in entrambi i modi.
Un heap ti consente di risparmiare un livello di riferimento indiretto. Gli indici contengono ID di riga, che puntano direttamente (bene, non proprio, ma il più direttamente possibile) a una posizione del disco. Pertanto, una ricerca di indice contro un heap dovrebbe costare all'incirca la metà di una ricerca di indice non cluster contro una tabella di cluster.
Un indice cluster viene ordinato, di per sé, grazie a un indice (quasi) libero. Poiché l'indice di clustering si riflette nell'ordine fisico dei dati, occupa relativamente poco spazio sopra i dati effettivi stessi, che ovviamente è necessario archiviare comunque. Poiché è fisicamente ordinato, una scansione dell'intervallo rispetto a questo indice può cercare il punto iniziale e quindi comprimere in modo molto efficiente il punto finale.
Gli indici sui cumuli di riferimento di heap, che sono 64 bit. Come accennato, gli indici non cluster su una tabella cluster fanno riferimento alla chiave di clustering, che può essere più piccola (un 32 bit INT
), uguale (un 64 bit BIGINT
) o più grande (un 48 bit DATETIME2()
più un 32 bit INT
, o un GUID a 128 bit). Ovviamente un riferimento più ampio rende indici più grandi e più costosi.
Requisiti di spazio
Con queste due tabelle:
CREATE TABLE TmpClustered
(
ID1 INT NOT NULL,
ID2 INT NOT NULL
)
ALTER TABLE TmpClustered ADD CONSTRAINT PK_Tmp1 PRIMARY KEY CLUSTERED (ID1)
CREATE UNIQUE INDEX UQ_Tmp1 ON TmpClustered (ID2)
CREATE TABLE TmpNonClustered
(
ID1 INT NOT NULL,
ID2 INT NOT NULL
)
ALTER TABLE TmpNonClustered ADD CONSTRAINT PK_Tmp2 PRIMARY KEY NONCLUSTERED (ID1)
CREATE UNIQUE INDEX UQ_Tmp2 ON TmpNonClustered (ID2)
... ciascuno popolato con 8,7 M di record, lo spazio richiesto era di 150 MB per i dati per entrambi; 120 MB per gli indici della tabella cluster, 310 MB per gli indici della tabella non cluster. Ciò riflette che l'indice cluster è più stretto di un RID e che l'indice cluster è principalmente un "omaggio". Senza gli indici univoci attivi ID2
, lo spazio dell'indice richiesto scende a 155 MB per la tabella non cluster (metà, come ci si aspetterebbe), ma solo 150 KB per il PK cluster: quasi nulla.
Quindi un indice non cluster di un campo a 32 bit in una tabella cluster con un indice a 32 bit (totale 64 bit, nominalmente) ha richiesto 120 MB, mentre un indice di un campo a 32 bit in un heap con 64 bit Il RID (in totale 96 bit, nominalmente) ha richiesto 155 MB, un po 'meno dell'aumento del 50% che ci si aspetterebbe ingenuamente di passare da 64 a 96 bit, ma ovviamente c'è un overhead che riduce l'effettiva differenza di dimensioni.
Il popolamento delle due tabelle e la creazione dei loro indici ha richiesto lo stesso tempo per ciascuna tabella. Eseguendo semplici test che coinvolgono scansioni o ricerche, non ho trovato differenze sostanziali di prestazioni tra le tabelle, che corrispondono al white paper di Microsoft che gbn ha collegato utile. Detto documento mostra una differenza significativa per l'accesso altamente concorrenziale; Non sono sicuro del perché ciò accada, si spera che qualcuno con più esperienza di me con sistemi OLTP ad alto volume possa dircelo.
L'aggiunta di ~ 40 byte di dati casuali di lunghezza variabile non ha modificato sensibilmente questa equivalenza. Nemmeno la sostituzione della INT
s con UUID ampi (ogni tabella è stata rallentata all'incirca nella stessa misura). La vostra situazione potrebbe essere diversa, ma nella maggior parte dei casi se un indice è disponibile è più importante di che tipo.
Pezzi e pezzi
Eseguendo una scansione di intervallo su un indice non cluster - sia perché la tabella è un heap o l'indice non è l'indice cluster - comporta la scansione dell'indice e quindi una ricerca sulla tabella per ogni hit. Questo può essere molto costoso, quindi a volte è più economico scansionare il tavolo. Tuttavia, puoi aggirare questo problema con un indice di copertura. Questo vale indipendentemente dal fatto che tu abbia raggruppato o meno il tuo tavolo.
Come ha sottolineato @gbn, non esiste un modo semplice per compattare un heap. Tuttavia, se la tabella aumenta gradualmente nel tempo - un caso molto comune - ci saranno pochi sprechi poiché lo spazio liberato dalle eliminazioni sarà riempito da nuovi dati.
Molte delle discussioni tra heap e tabelle raggruppate che ho visto fanno una curiosa discussione di paglia che un heap senza indici sia inferiore a una tabella cluster in quanto richiede sempre una scansione della tabella. Questo è certamente vero, ma il confronto più significativo è "grande tabella cluster ben indicizzata" vs "grande heap ben indicizzato". Se il tuo tavolo è molto piccolo o eseguirai sempre scansioni, non importa molto se lo cluster o no.
Poiché ogni indice in una tabella cluster fa riferimento all'indice cluster, sono in effetti tutti gli indici di copertura. Una query che fa riferimento a una colonna indicizzata e alle colonne del cluster può eseguire una scansione dell'indice senza alcuna ricerca nella tabella. Questo in genere non è utile se il tuo indice di clustering è una chiave sintetica, ma se è una chiave di business che dovresti comunque recuperare, è una bella funzionalità.
TL; DR
Sono un ragazzo di data warehousing, non un esperto OLTP. Per le tabelle di fatto, utilizzo quasi sempre un indice di clustering sul campo, che probabilmente avrà bisogno di scansioni di intervalli, in genere un campo data. Per le tabelle dimensionali, eseguo il clustering sul PK, quindi è preordinato per unire join con tabelle fact.
Esistono diversi motivi per utilizzare gli indici di clustering, ma se nessuna di queste ragioni si applica, il sovraccarico potrebbe non essere utile. Ho il sospetto che ci siano molti "l'abbiamo sempre fatto in questo modo" ed "è solo una buona pratica" dietro le persone che usano universalmente gli indici cluster. Prova entrambi con i tuoi dati e il tuo carico e vedi cosa funziona meglio.