L'ordine delle colonne in un indice PK è importante?


33

Ho alcuni tavoli molto grandi con la stessa struttura di base. Ognuno ha una RowNumber (bigint)e una DataDate (date)colonna. I dati vengono caricati utilizzando SQLBulkImport ogni notte e non vengono mai caricati dati "nuovi": è un record storico (SQL Standard, non Enterprise, quindi nessun partizionamento).

Poiché ogni bit di dati deve essere ricollegato ad altri sistemi e ogni RowNumber/DataDatecombinazione è unica, questa è la mia chiave primaria.

Ho notato che a causa del modo in cui ho definito il PK in Progettazione tabelle SSMS, RowNumberè elencato prima e DataDateseconda.

Noto anche che la mia frammentazione è sempre MOLTO alta ~ 99%.

Ora, poiché ognuno DataDateappare una sola volta, mi aspetto che l'indicizzatore si aggiunga alle pagine ogni giorno, ma mi chiedo se in realtà si sta indicizzando in base al RowNumberprimo, e quindi dovendo spostare tutto il resto?


Rownumbernon è una colonna di identità, è un int generato da un sistema esterno (purtroppo). Si reimposta all'inizio di ciascuno DataDate.

Dati di esempio

RowNumber | DataDate | a | b | c..... 
   1      |2013-08-01| x | y | z 
   2      |2013-08-01| x | y | z 
...
   1      |2013-08-02| x | y | z 
   2      |2013-08-02| x | y | z 
...

I dati vengono caricati in RowNumberordine, uno DataDateper carico.

Il processo di importazione è bcp - ho provato a caricare su una tabella temporanea e quindi selezionando in ordine da lì ( ORDER BY RowNumber, DataDate) ma esce comunque alta frammentazione.

Risposte:


50

L'ordine delle colonne in un indice PK è importante?

Sì lo fa.

Per impostazione predefinita, il vincolo della chiave primaria viene applicato in SQL Server da un indice cluster univoco. L'indice cluster definisce l' ordine logico delle righe nella tabella. Potrebbero esserci un numero di pagine di indice extra aggiunte per rappresentare i livelli superiori dell'indice b-tree, ma il livello più basso (foglia) di un indice cluster è semplicemente l'ordine logico dei dati stessi.

Per essere chiari a questo proposito, le righe su una pagina non sono necessariamente archiviate fisicamente nell'ordine delle chiavi dell'indice cluster. C'è una struttura di indiretta separata all'interno della pagina che memorizza un puntatore a ogni riga. Questa struttura è ordinata in base alle chiavi di indice cluster. Inoltre, ogni pagina ha un puntatore alla pagina precedente e successiva allo stesso livello in ordine di chiave indice cluster.

Con una chiave primaria cluster di (RowNumber, DataDate), le righe vengono logicamente ordinate prima per RowNumbere poi per DataDate- quindi tutte le righe dove RowNumber = 1sono raggruppate logicamente insieme, quindi le righe dove RowNumber = 2e così via.

Quando si aggiungono nuovi dati (con RowNumbersda 1 a n) le nuove righe appartengono logicamente alle pagine esistenti, quindi SQL Server probabilmente dovrà fare molto lavoro per dividere le pagine per fare spazio. Tutta questa attività genera molto lavoro extra (inclusa la registrazione delle modifiche) senza alcun guadagno.

Anche le pagine divise iniziano vuote per circa il 50%, quindi una divisione eccessiva può comportare una bassa densità di pagina (meno righe dell'ottimale per pagina). Questa brutta notizia non è solo per la lettura dal disco (densità inferiore = più pagine da leggere), ma anche le pagine a densità inferiore occupano più spazio in memoria quando vengono memorizzate nella cache.

La modifica dell'indice cluster in (DataDate, RowNumber) significa che i nuovi dati (con, presumibilmente, più elevati di DataDatesquelli attualmente memorizzati) vengono aggiunti alla fine logica dell'indice cluster su nuove pagine. Ciò rimuoverà le spese generali non necessarie della divisione delle pagine e si tradurrà in tempi di caricamento più rapidi. Dati meno frammentati significano anche che l'attività read-ahead (lettura di pagine dal disco appena prima che siano necessarie per una query in corso) può essere più efficiente.

Se non altro, le query sono molto più probabile per cercare DataDatedi RowNumber. Un indice cluster attivato (DataDate, RowNumber) supporta l'indice cerca DataDate(e quindi RowNumber). La disposizione esistente supporta solo le ricerche su RowNumber(e solo allora, forse, su DataDate). Potresti riuscire a rilasciare l'indice non cluster esistente DataDateuna volta modificata la chiave primaria. L'indice cluster sarà più ampio dell'indice non cluster che sostituisce, quindi è necessario verificare per garantire che le prestazioni rimangano accettabili.

Quando si importano nuovi dati con bcp, è possibile ottenere prestazioni più elevate se i dati all'interno del file di importazione vengono ordinati in base alle chiavi di indice cluster (idealmente (DataDate, RowNumber)) e si specifica l' bcpopzione:

-h "ORDER(DataDate,RowNumber), TABLOCK"

Per prestazioni ottimali di caricamento dei dati, è possibile provare a ottenere inserti con registrazione minima. Per ulteriori informazioni, vedere:


4
Una risposta eccellente: ora so COSA dovrei fare E perché. Lo avevo pensato, ma non lo so! Grazie.
BlueChippy,

Ho impiegato un po 'di tempo per ottenere il DB nel mio SQL Server locale per il test: prima di modificare il carico dell'indice ci sono voluti 45 minuti ... dopo, ci sono voluti solo 5 !!!
BlueChippy,

13

Sì, l'ordine è fondamentale. Dubito fortemente che tu abbia mai cercato da RowNumber (ad es WHERE RowNumber=1.). Le serie temporali sono in gran parte richieste per data ( WHERE DataDate BEWEEN @start AND @end) e tali query richiederebbero un'organizzazione raggruppata per DataDate.

La frammentazione in generale è un'aringa rossa. Ridurre la frammentazione non dovrebbe essere il tuo obiettivo qui, ma dovrebbe avere un'organizzazione adeguata per le tue domande. Inoltre, ottenere una frammentazione ridotta è una buona idea, ma non è un obiettivo da solo. Se disponi di un modello di dati adeguatamente organizzato che corrisponde al tuo carico di lavoro (le tue query sono adeguatamente coperte) e disponi di misurazioni che mostrano la frammentazione come un impatto sulle prestazioni, possiamo parlarne.


Ho anche un indice non cluster su DataDate, che come dici è spesso una WHEREclausola nelle query.
BlueChippy,

1
Se l'ORDINE delle colonne è critico, l'impatto dell'ordine errato vedrebbe aumentare il mio I / O? Il mio pensiero è che sta ordinando da RowNumber e che quindi debba fare molto lavoro sugli indici ogni volta, mentre dovrebbe essere basato su DataDate?
BlueChippy,
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.