L'indice su una colonna di identità deve essere non cluster?


19

Per una tabella con colonna Identity, è necessario creare un indice PK / univoco cluster o non cluster per la colonna Identity?

Il motivo è che verranno creati altri indici per le query. Una query che utilizza un indice non cluster (su un heap) e restituisce colonne che non sono coperte dall'indice utilizzerà un I / O logico (LIO) meno logico perché non sono presenti ulteriori passaggi di ricerca b-tree nell'indice cluster?

create table T (
  Id int identity(1,1) primary key, -- clustered or non-clustered? (surrogate key, may be used to join another table)
  A .... -- A, B, C have mixed data type of int, date, varchar, float, money, ....
  B ....
  C ....
  ....)

create index ix_A on T (A)
create index ix_..... -- Many indexes can be created for queries

-- Common query is query on A, B, C, ....
select A, B 
from T 
where A between @a and @a+5 -- This query will have less LIO if the PK is non-clustered (seek)

select A, B, C
from T 
where B between @a and @a+5 

....

PK in cluster nella colonna identità è buono perché:

  1. Aumenta in modo monotono quindi non si divide la pagina durante l'inserimento. Si dice che un inserto di massa può essere veloce come su una tabella heap (non cluster)

  2. È stretto

Tuttavia, le query nella domanda saranno più veloci senza impostarle in cluster?

** Aggiornamento: ** Cosa succede se l' IdFK è di altre tabelle e verrà aggiunto in alcune query?


3
Non è meglio o peggio, dipende.
Aaron Bertrand

1
@ypercube Il link kejser.org/clustered-indexes-vs-heaps ha detto che il non-CI avrà meno LIO.
u23432534

2
Ho letto l'articolo in passato e indica certamente che ci sono casi per un indice cluster e casi per un heap. Non è tutto nero o tutto bianco.
ypercubeᵀᴹ

4
Non sono sicuro che la tua risposta a @ypercube soddisfi uno dei criteri citati da Kejser, almeno con i dettagli che hai condiviso. Nella sua forma attuale, in realtà non sono sicuro che questo genererà una risposta utile perché dovrebbe coprire quasi ogni singolo scenario, cosa che è già stata fatta nel post sul blog che hai citato. Se puoi fornire maggiori dettagli sul tuo scenario specifico, forse alcune delle conoscenze nel post possono essere applicate.
sweckeck

2
Dipenderà da cose come: a) carico di lavoro (OLTP? OLAP? Ecc.), B) dimensioni della tabella, c) forma normale, solo per citarne alcune. Non hai fornito dettagli su nessuno di questi fattori, quindi qualsiasi raccomandazione si baserebbe su ipotesi del tuo ambiente. Inoltre, hai provato a profilare le query che stai proponendo (con buffer cancellati) e ottenere i profili I / O specifici per configurazione e vederli di persona?
cambio il

Risposte:


16

Per impostazione predefinita, il PK è cluster e nella maggior parte dei casi, va bene. Tuttavia, quale domanda dovrebbe essere posta:

  • il mio PK dovrebbe essere raggruppato?
  • quali colonne saranno la chiave migliore per il mio indice cluster?

PK e indice cluster sono 2 cose differenze:

  • PK è un vincolo. PK viene utilizzato per identificare in modo univoco le righe, ma non esiste alcuna nozione di memoria. Tuttavia, per impostazione predefinita (in SSMS), viene applicato da un indice cluster univoco se non è ancora presente un indice cluster.
  • Gli indici cluster sono un tipo speciale di indice che memorizza i dati delle righe a livello foglia, il che significa che copre sempre. Tutte le colonne, che facciano parte o meno della chiave, sono memorizzate a livello foglia. Non deve essere univoco, nel qual caso viene aggiunto un unificatore (4 byte) alla chiave del cluster.

Ora finiamo con 2 domande:

  • Come desidero identificare in modo univoco le righe nella mia tabella (PK)
  • Come voglio memorizzarlo a livello foglia di un indice (Clustered Index)

Dipende da come:

  • progettate il vostro modello di dati
  • richiedi i tuoi dati e scrivi le tue domande
  • inserisci o aggiorni i tuoi dati
  • ...

Innanzitutto, hai bisogno di un indice cluster? Se si inserisce in blocco, è più efficiente archiviare i dati non ordinati in un HEAP (rispetto ai dati ordinati in un cluster). Utilizza RID (identificatore di riga, 8 byte) per identificare in modo univoco le righe e memorizzarle nelle pagine.

L'indice cluster non dovrebbe essere un valore casuale. I dati a livello foglia saranno memorizzati e ordinati dalla chiave indice. Pertanto dovrebbe crescere continuamente per evitare la frammentazione o la divisione della pagina. Se questo non può essere raggiunto dal PK, è necessario considerare un'altra chiave come candidato raggruppato. L'indice cluster su colonne identy, GUID sequenziali o anche qualcosa di simile alla data di inserimento va bene da un punto di vista sequenziale poiché tutte le righe verranno aggiunte all'ultima pagina foglia. D'altra parte, mentre l'identificatore univoco può essere utile per le esigenze della tua azienda come PK, non dovrebbero essere raggruppati (sono ordinati / generati casualmente).

Se dopo l'analisi di alcuni dati e query, scopri di utilizzare principalmente lo stesso indice per ottenere i tuoi dati prima di eseguire una ricerca chiave nel PK cluster, puoi considerarlo come indice cluster anche se potrebbe non identificare in modo univoco i tuoi dati.

La chiave di indice cluster è composta da tutte le colonne che si desidera indicizzare. Una colonna uniquefier (4 byte) viene aggiunta se non vi è alcun vincolo univoco su di essa (valore incrementale per i duplicati, null altrimenti). Questa chiave di indice verrà quindi memorizzata una volta per ogni riga a livello foglia di tutti gli indici non cluster. Alcuni di essi verranno anche memorizzati più volte a livelli intermedi (ramo) tra la radice e il livello foglia dell'albero indice (albero B). Se la chiave è troppo grande, tutto l'indice non cluster diventerà più grande, richiederà più spazio di archiviazione e più IO, CPU, memoria, ... Se hai un PK su nome + data di nascita + paese, è molto probabile che questa chiave non è un buon candidato. È troppo grande per un indice cluster. L'identificatore univoco che utilizza NEWSEQUENTIALID () non viene in genere considerato come una chiave ristretta (16 byte) sebbene sia sequenziale.

Quindi, una volta capito come identificare in modo univoco le righe nella tabella, puoi aggiungere un PK. Se pensi di non usarlo nella tua query, non crearlo in cluster. Puoi comunque creare un altro indice non cluster se a volte devi interrogarlo. Si noti che il PK creerà automaticamente un indice univoco.

Gli indici non cluster conterranno sempre la chiave cluster. Tuttavia, se le colonne indicizzate (+ colonne chiave) sono coperte, non ci sarà alcuna ricerca chiave nell'indice cluster. Non dimenticare che puoi anche aggiungere Includi e Dove a un indice non cluster. (usalo saggiamente)

L'indice cluster dovrebbe essere unico e il più stretto possibile L'indice cluster non dovrebbe cambiare nel tempo e dovrebbe essere inserito in modo incrementale.

Ora è il momento di scrivere un po 'di SQL che creerà la tabella, indici e vincoli cluster e non cluster.

Tutto ciò è teorico perché non conosciamo il modello di dati e i tipi di dati utilizzati (A e B).


11

Per una tabella con una chiave primaria (PK) su una colonna di identità, verrà raggruppata per impostazione predefinita. Potrebbe essere meglio come non cluster?

Se stai chiedendo se il valore predefinito per una chiave primaria su una colonna di identità (in particolare) debba essere non cluster, direi di no. La maggior parte delle tabelle beneficia di un indice cluster, quindi rendere il cluster predefinito per un vincolo di chiave primaria è probabilmente utile in generale, soprattutto per i nuovi utenti di SQL Server.

Come con qualsiasi altra opzione, ci sono sempre diverse circostanze in cui uno deve essere preferito rispetto all'altro, ma un DBA esperto dovrebbe essere a conoscenza del valore predefinito ed essere in grado di sovrascriverlo quando appropriato. Vedi anche le relative domande e risposte, quando una chiave primaria deve essere dichiarata non cluster? .

Le query nella domanda saranno più veloci senza impostarle in cluster?

Sì, ma con avvertenze.

Le ricerche RID sono effettivamente più efficienti delle ricerche chiave. Anche se tutte le pagine richieste sono in memoria (molto probabilmente per i livelli superiori di un indice), esiste un costo della CPU associato alla navigazione dell'albero b dell'indice cluster. Di conseguenza, SQL Server può in genere eseguire molte più ricerche RID rispetto alle ricerche chiave per unità di tempo della CPU.

Avvertenze

Quanto sopra non sarebbe spesso un fattore determinante quando si decide se strutturare una tabella come un heap o meno. Dovrebbe essere poco pratico evitare le ricerche (usando gli indici di copertura) e il numero di ricerche dovrebbe essere abbastanza grande da avere un effetto misurabile (e importante) sulle prestazioni, dato l'ambiente hardware e il carico di lavoro.

In questa risposta non è davvero pratico coprire tutti gli aspetti del dibattito tra heap e indice cluster, ma dirò che ci sono relativamente pochi buoni motivi per preferire strutturare una tabella come un heap in generale. Per me, scegliere il tipo di design proposto nella domanda richiederebbe un'analisi molto attenta prima dell'implementazione e dovrebbe incontrare un livello elevato. Argomenti generali sulla "scalabilità" non sarebbero sufficienti.

Per quanto riguarda l'aggiornamento alla domanda sui join, la valutazione dell'impatto della perdita dell'indice cluster sui piani di esecuzione farebbe parte dell'analisi sopra menzionata. Se vengono utilizzati join loop nidificati, è molto comodo avere l'indice cluster sulla chiave di join perché tutte le colonne della riga sono immediatamente disponibili senza una ricerca.

La mia esperienza personale è stata che avere indici cluster univoci su colonne di identità è molto spesso vantaggioso, tutto considerato. Ho trovato molti problemi in termini di gestione dello spazio e dovrei anche menzionare che alcune funzionalità di SQL Server richiedono un indice cluster univoco per funzionare.


8

In realtà, non è necessario creare né un indice cluster né una chiave primaria, poiché gli indici univoci e gli indici non univoci possono gestire il lavoro. SQL Server supporta un indice cluster almeno dalla versione 1.1, ma la chiave primaria era solo un "concetto" che i programmatori applicavano definendo un indice univoco.

Ma sembra che sia le chiavi primarie sia gli indici cluster siano concetti preziosi nella maggior parte dei database.

Diamo un'occhiata alla documentazione di SQL Server per vedere le descrizioni parziali di alcune opzioni di indicizzazione come mostrato di seguito.

Indice cluster: https://msdn.microsoft.com/en-us/library/ms190457.aspx

  • Gli indici cluster ordinano e memorizzano le righe di dati nella tabella o nella vista in base ai loro valori chiave. Queste sono le colonne incluse nella definizione dell'indice.
  • Può esserci un solo indice cluster per tabella

Chiave primaria: https://msdn.microsoft.com/en-us/library/ms190457.aspx

  • Una tabella può contenere solo un vincolo PRIMARY KEY.

  • Tutte le colonne definite all'interno di un vincolo PRIMARY KEY devono essere definite come NOT NULL.

  • La chiave primaria può essere creata come un indice cluster (impostazione predefinita se non esiste un indice cluster) o un indice non cluster.

Indice univoco: https://msdn.microsoft.com/en-us/library/ms187019.aspx

  • Quando si crea un vincolo UNIQUE, viene creato un indice univoco non cluster per imporre un vincolo UNIQUE per impostazione predefinita.

  • È possibile specificare un indice cluster UNIQUE se un indice cluster non esiste già per la tabella.

Ciò significa che la tua domanda sugli indici cluster e sulle chiavi primarie riguarda davvero alcuni dei seguenti problemi. Si noti che non tutte le tabelle beneficiano dello stesso piano di indicizzazione.

Quando trarrebbe beneficio dalla separazione della chiave primaria dall'indice cluster?

Forse quando l'indice cluster è ampio (ad esempio, 5 colonne di informazioni testuali, ma la chiave primaria è piccola (INT o BIGINT), come sembra che tu stia descrivendo.

  • Un ampio indice cluster consentirebbe di selezionare rapidamente le righe dall'indice per un sottoinsieme di query che forniscono risposte seriali dall'indice cluster (noto anche come tabella ). Ad esempio, un indice cluster a 5 colonne supporterebbe la scansione delle colonne C1, C2, C3, C4, C5 o C1, C2, C3, C4 e così via fino a C1.
  • Nota: se le righe erano grandi, ciò potrebbe offrire alcuni vantaggi in termini di velocità nella selezione del set seriale di righe, soprattutto se altre colonne nella tabella vengono regolarmente incluse nel set di risultati.
  • In tal caso è possibile utilizzare la chiave primaria per l'integrità referenziale al fine di fornire il valore necessario come chiave esterna per vincolare le righe in altre tabelle. Il PK è piccolo ed è quindi l'FK un piccolo successo sulla dimensione delle tabelle di riferimento.
  • Tuttavia, si noti che qualsiasi indice creato su una tabella con un indice cluster includerà tutte le colonne del cluster negli altri indici creati in questa tabella. Un ampio indice cluster aumenterebbe la dimensione di tutti gli indici non cluster su quella tabella.

Dovresti fare in modo che la chiave primaria da sola sia l'indice cluster?

  • Se si dispone di una chiave primaria piccola (INT o BIGINT) ed è l'indice cluster, l'overhead delle colonne del cluster è relativamente piccolo. Sebbene la chiave primaria cluster in questo caso esista anche in tutti gli indici di questa tabella, è un prezzo inferiore da pagare rispetto al cluster largo di cui sopra.

  • Questo indice cluster di chiavi primarie di solito non offre direttamente un percorso semplice per la selezione seriale di più righe.

  • Ora che hai creato una chiave primaria in cluster, che dire delle altre colonne che stavi pianificando di includere nell'indice in cluster ?

  • Creare un indice univoco (o non univoco) in base alle esigenze per indicizzare i criteri di ricerca ampia delle colonne C1, C2, C3, C4, C5. I valori in questo indice "Imitazione raggruppata" possono servire come percorso di ricerca più veloce per quelle 5 colonne. Se sono presenti anche una o due colonne non indicizzate che vengono regolarmente selezionate, possono essere incluse nell'indice con INCLUDE (Doctor_Name, Diagnosis_Synopsis).

Sebbene trovo utili semplici indici cluster e chiavi primarie, ci sono alcuni buoni motivi per pensare se usarli in una tabella o in un database.

Hai bisogno di un indice cluster?

  • Se si creano indici (indici univoci e indici non univoci) e si definisce la chiave primaria senza il sovraccarico di essere un indice cluster, è possibile che gli indici più stretti forniscano ciò di cui hai bisogno per le tue query.

  • Ci sono alcuni comportamenti utili negli indici cluster e nelle chiavi primarie, ma ricorda che sono proprio gli indici che contano di più. Progetta la strategia di indicizzazione per tenere conto delle realtà della tua applicazione. Forse è OneBigTablenecessario disporre di una strategia di indicizzazione diversa da quella utilizzata per la maggior parte delle tabelle.

  • Senza un indice cluster i dati verranno archiviati come un heap con l'identificatore di riga (RID) che non è affatto un buon meccanismo di ricerca. Ma, come accennato in precedenza, è possibile creare indici univoci e non unici per gestire le query.

Che ora ti porta a considerare Heaps:

Heaps and Indexes: https://msdn.microsoft.com/en-us/library/hh213609.aspx

  • Quando una tabella viene archiviata come heap, le singole righe vengono identificate facendo riferimento a un identificatore di riga (RID) costituito dal numero di file, dal numero di pagina di dati e dallo slot nella pagina. L'ID riga è una struttura piccola ed efficiente. (Ma non è un indice .)
  • A volte gli architetti di dati usano i cumuli quando si accede sempre ai dati tramite indici non cluster e il RID è più piccolo di una chiave di indice cluster .

Ma se hai anche alcuni 'hot spot' in un set di big data, puoi anche cercare un altro tipo di indice:

Indice filtrato: https://msdn.microsoft.com/en-us/library/cc280372.aspx

  • Un indice filtrato ben progettato migliora le prestazioni della query e la qualità del piano di esecuzione perché è più piccolo di un indice non cluster a tabella completa e dispone di statistiche filtrate. Le statistiche filtrate sono più accurate delle statistiche a tabella intera perché coprono solo le righe dell'indice filtrato .

  • Gli indici filtrati hanno una serie di restrizioni che sono delineate nel collegamento agli indici filtrati.

Tuttavia, se sei interessato a pensare a quella possibilità di saltare le chiavi primarie e gli indici cluster, potresti leggere il post di Markus Winand collegato di seguito. Dimostra le sue ragioni, con alcuni esempi di codice, per suggerire che a volte potrebbe essere una buona idea rinunciare a usare quelle funzionalità.

http://use-the-index-luke.com/blog/2014-01/unreasonable-defaults-primary-key-clustering-key

Ma tutto torna finalmente a comprendere la tua applicazione e progettare il codice, le tabelle, gli indici e così via per adattarsi al lavoro che stai facendo.


Per quello che vale, nel mio lavoro quotidiano se trovo una tabella che è un heap lo considero molto probabilmente un errore e controllo con gli sviluppatori per vedere se è stato creato un heap intenzionalmente.
RLF,

-2

Un paio di punti da considerare.

Mentre un indice (raggruppato o meno) su un valore monotono in aumento salva le divisioni di pagina durante gli inserimenti di massa, crea un nuovo hot spot alla fine dell'indice. Anche se potrebbe non essere un problema con un inserimento bulk a thread singolo, aumenterà sicuramente la contesa per un'applicazione multithread che inserisce nuove tuple a un ritmo elevato, poiché i thread competeranno costantemente per l'accesso all'ultima pagina dell'indice.

Raggruppare la tabella in base a un PK surrogato (identità) è raramente vantaggioso. Tale chiave primaria viene utilizzata principalmente per accedere a singole tuple, una alla volta o per scansionare l'intero indice alla ricerca di join. In entrambi i casi, non importa se l'indice è raggruppato o meno (ad eccezione dei join di unione, può essere, ma con che frequenza sono?)

Penso che trarrai maggiori benefici da un indice cluster che copre le query che richiedono una scansione dell'intervallo di chiavi e predicati aggiuntivi che fanno riferimento ad altre colonne.


Quanto deve essere elevato il tasso affinché questo diventi effettivamente un problema?
ypercubeᵀᴹ

@ypercube posso dire "dipende"? Perché lo fa. In assenza di trigger sul tavolo, mi aspetto di iniziare a sperimentare una contesa con una dozzina di thread per un totale di 1K inserti al secondo.
Mustaccio,


Non sono in disaccordo, ma chiedevo fino a che punto si può andare con un singolo punto caldo. Ricordo di aver visto un articolo sull'inserimento di 30K righe al secondo in una tabella con IDENTITY come elemento della configurazione (se la memoria mi serve bene) ma non riesco a trovare il post del blog.
ypercubeᵀᴹ

Questa discussione è inutile in assenza di un carico di lavoro concreto in esecuzione su uno schema concreto su hardware specifico. Spero che tutti possiamo essere d'accordo sul fatto che un indice su una sequenza monotona in aumento creerà un "punto caldo"; se creerà un collo di bottiglia inaccettabile e se ci si dovrebbe preoccupare o meno dipende dalle circostanze.
Mustaccio,
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.