Ist die Reihenfolge der Spalten in einem PK-Index wichtig?

33

Ich habe ein paar sehr große Tische mit der gleichen Grundstruktur. Jeder hat eine RowNumber (bigint)und DataDate (date)Spalte. Daten werden jede Nacht mit SQLBulkImport geladen, und es werden nie "neue" Daten geladen - es handelt sich um einen historischen Datensatz (SQL Standard, nicht Enterprise, also keine Partitionierung).

Da jedes Datenbit mit anderen Systemen verknüpft werden muss und jede RowNumber/DataDateKombination einzigartig ist, ist dies mein Primärschlüssel.

Ich stelle fest, dass aufgrund der Art und Weise, wie ich die PK im SSMS-Tabellen-Designer definiert habe, RowNumberzunächst und an DataDatezweiter Stelle aufgeführt wird.

Ich bemerke auch, dass meine Fragmentierung immer SEHR hoch ist ~ 99%.

Nun, da jeder DataDatenur einmal erscheint, würde ich erwarten, dass der Indexer jeden Tag nur die Seiten erweitert, aber ich frage mich, ob er tatsächlich auf dem RowNumberersten Index basiert und daher alles andere verschieben muss.


Rownumberist keine Identitätsspalte, sondern ein Int, der (leider) von einem externen System generiert wird. Es wird jeweils zu Beginn zurückgesetzt DataDate.

Beispieldaten

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 
...

Die Daten werden der RowNumberReihe nach geladen , eine DataDatepro Ladung.

Importprozess ist bcp - Ich habe versucht, in eine temporäre Tabelle zu laden und dann in der Reihenfolge von dort ( ORDER BY RowNumber, DataDate) auszuwählen, aber es kommt immer noch eine hohe Fragmentierung heraus.

BlueChippy
quelle

Antworten:

50

Ist die Reihenfolge der Spalten in einem PK-Index wichtig?

Ja tut es.

Standardmäßig wird die Primärschlüsseleinschränkung in SQL Server durch einen eindeutigen Clustered-Index erzwungen. Der Clustered-Index definiert die logische Reihenfolge der Zeilen in der Tabelle. Möglicherweise wird eine Reihe zusätzlicher Indexseiten hinzugefügt, um die oberen Ebenen des B-Tree-Index darzustellen. Die unterste (Blatt-) Ebene eines Clustered-Index ist jedoch einfach die logische Reihenfolge der Daten.

Um dies zu verdeutlichen, werden Zeilen auf einer Seite nicht unbedingt physisch in der Reihenfolge der gruppierten Indexschlüssel gespeichert. Es gibt eine separate Indirektionsstruktur innerhalb der Seite, die einen Zeiger auf jede Zeile speichert. Diese Struktur ist nach den gruppierten Indexschlüsseln sortiert. Außerdem hat jede Seite einen Zeiger auf die vorherige und nächste Seite auf derselben Ebene in der Reihenfolge der gruppierten Indexschlüssel.

Mit einem gruppierten Primärschlüssel von (RowNumber, DataDate) werden die Zeilen zuerst nach RowNumberund dann nach logisch sortiert DataDate- also alle Zeilen, in denen sie RowNumber = 1logisch gruppiert sind, dann Zeilen, in denen RowNumber = 2usw.

Wenn Sie neue Daten (mit RowNumbers1 bis n) hinzufügen , gehören die neuen Zeilen logischerweise zu den vorhandenen Seiten, sodass SQL Server wahrscheinlich viel Arbeit aufteilen muss, um Platz zu schaffen. All diese Aktivitäten verursachen eine Menge zusätzlicher Arbeit (einschließlich des Protokollierens der Änderungen) ohne Gewinn.

Geteilte Seiten beginnen ebenfalls zu 50% leer, sodass eine übermäßige Teilung auch zu einer geringen Seitendichte führen kann (weniger Zeilen als optimal pro Seite). Dies ist nicht nur eine schlechte Nachricht für das Lesen von Datenträgern (geringere Dichte = mehr zu lesende Seiten), die Seiten mit geringerer Dichte belegen auch mehr Speicherplatz im Cache.

Ändern des Clustered-Index in (DataDate, RowNumber ) ändern, werden neue Daten (die vermutlich höher sind DataDatesals die derzeit gespeicherten) auf neuen Seiten an das logische Ende des Clustered-Index angehängt. Dies beseitigt den unnötigen Overhead beim Teilen von Seiten und führt zu schnelleren Ladezeiten. Weniger fragmentierte Daten bedeuten auch, dass Vorausleseaktivitäten (Lesen von Seiten von der Festplatte, kurz bevor sie für eine laufende Abfrage benötigt werden) effizienter sind.

Wenn nichts anderes, suchen Ihre Abfragen viel häufiger nach DataDateals RowNumber. Ein Clustered-Index für (DataDate, RowNumberunterstützt Index-Suchvorgänge für DataDate(und dann RowNumber). Die bestehende Anordnung unterstützt nur Suchvorgänge RowNumber(und möglicherweise erst dann DataDate). Möglicherweise können Sie den vorhandenen nicht gruppierten Index auf löschenDataDate sobald der Primärschlüssel geändert wird. Der Clustered-Index ist breiter als der Nonclustered-Index, den er ersetzt. Sie sollten daher testen, um sicherzustellen, dass die Leistung akzeptabel bleibt.

Wenn Sie neue Daten mit importieren bcp, erhalten Sie möglicherweise eine höhere Leistung, wenn die Daten in der Importdatei (idealerweise (DataDate, RowNumber) nach den gruppierten Indexschlüsseln sortiert sind und Sie die folgende bcpOption angeben :

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

Um die beste Datenladeleistung zu erzielen, sollten Sie versuchen, minimal protokollierte Einfügungen zu erzielen. Weitere Informationen finden Sie unter:

Paul White sagt GoFundMonica
quelle
4
Eine ausgezeichnete Antwort - ich weiß jetzt, WAS ich tun soll UND warum. Ich hatte es gedacht, aber nicht so bekannt! Vielen Dank.
BlueChippy
Es dauerte LOOOOONG, bis die Datenbank in meinem lokalen SQL Server zum Testen verfügbar war: Bevor das Laden des Indexes geändert wurde, dauerte es 45 Minuten ... danach waren es nur noch 5 !!!
BlueChippy
13

Ja, die Reihenfolge ist kritisch. Ich bezweifle sehr, dass Sie jemals eine Anfrage nach RowNumber (zB WHERE RowNumber=1) stellen. Überwiegend werden Zeitreihen nach Datum ( WHERE DataDate BEWEEN @start AND @end) abgefragt, und solche Abfragen würden eine gruppierte Organisation nach erfordern DataDate.

Fragmentierung im Allgemeinen ist ein roter Hering. Die Reduzierung der Fragmentierung sollte hier nicht Ihr Ziel sein, aber eine ordnungsgemäße Organisation für Ihre Abfragen sollte es sein. Darüber hinaus ist es eine gute Idee, eine geringere Fragmentierung zu erreichen, aber kein eigenständiges Ziel. Wenn Sie über ein ordnungsgemäß organisiertes Datenmodell verfügen, das Ihrer Arbeitsauslastung entspricht (Ihre Abfragen werden ordnungsgemäß behandelt), und wenn Sie Messungen haben, die Fragmentierung als Auswirkung auf die Leistung anzeigen, können wir darüber sprechen.

Remus Rusanu
quelle
Ich habe auch einen oder mehrere nicht gruppierte Indizes für DataDate, was, wie Sie sagen WHERE, in Abfragen häufig als Klausel gilt.
BlueChippy
1
Wenn die Reihenfolge der Spalten kritisch ist, würde sich die Auswirkung der inkorrekten Reihenfolge auf meine E / A auswirken? Mein Gedanke ist, dass es nach RowNumber bestellt wird und daher jedes Mal eine Menge Arbeit an den Indizes machen muss, während es auf DataDate basieren sollte?
BlueChippy