it-swarm.com.de

Ist die Reihenfolge der Spalten in einem PK-Index von Bedeutung?

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

Weil jedes Datenbit mit anderen Systemen verknüpft werden muss und jedes RowNumber/DataDate Kombination ist einzigartig, das ist mein Primärschlüssel.

Ich stelle fest, dass aufgrund der Art und Weise, wie ich die PK in SSMS Table Designer definiert habe, RowNumber zuerst und DataDate an zweiter Stelle aufgeführt wird.

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

Da jedes DataDate nur einmal angezeigt wird, würde ich erwarten, dass der Indexer jeden Tag nur zu den Seiten hinzugefügt wird, aber ich frage mich, ob er tatsächlich zuerst auf der Grundlage von RowNumber indiziert und daher muss alles andere verschieben?


Rownumber ist keine Identitätsspalte, sondern ein Int, das (leider) von einem externen System generiert wird. Es wird zu Beginn jedes DataDate zurückgesetzt.

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 in der Reihenfolge RowNumber geladen, eine DataDate pro Ladung.

Der Importvorgang ist bcp - Ich habe versucht, in eine temporäre Tabelle zu laden und dann in der Reihenfolge von dort aus auszuwählen (ORDER BY RowNumber, DataDate) kommt aber immer noch stark fragmentiert heraus.

34
BlueChippy

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 werden mehrere zusätzliche Indexseiten hinzugefügt, um die oberen Ebenen des B-Tree-Index darzustellen. Die niedrigste (Blatt-) Ebene eines Clustered-Index ist jedoch einfach die logische Reihenfolge der Daten selbst.

Um es klar auszudrücken, werden Zeilen auf einer Seite nicht unbedingt physisch in der Reihenfolge der gruppierten Indexschlüssel gespeichert. Innerhalb der Seite gibt es eine separate Indirektionsstruktur, in der ein Zeiger auf jede Zeile gespeichert ist. Diese Struktur ist nach den gruppierten Indexschlüsseln sortiert. Außerdem verfügt jede Seite über einen Zeiger auf die vorherige und die nächste Seite auf derselben Ebene in der Reihenfolge der gruppierten Indexschlüssel.

Bei einem gruppierten Primärschlüssel von (RowNumber, DataDate) Werden die Zeilen zuerst logisch nach RowNumber und dann nach DataDate sortiert - also alle Zeilen, in denen RowNumber = 1 Logisch sind gruppiert, dann Zeilen, in denen RowNumber = 2 und so weiter.

Wenn Sie neue Daten hinzufügen (mit RowNumbers von 1 bis n), 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 viel zusätzliche Arbeit (einschließlich der Protokollierung der Änderungen) ohne Gewinn.

Geteilte Seiten beginnen ebenfalls zu etwa 50% leer, sodass übermäßiges Teilen auch zu einer geringen Seitendichte führen kann (weniger Zeilen als optimal pro Seite). Dies sind nicht nur schlechte Nachrichten für das Lesen von der Festplatte (geringere Dichte = mehr zu lesende Seiten), die Seiten mit geringerer Dichte beanspruchen auch mehr Speicherplatz, wenn sie zwischengespeichert werden.

Das Ändern des Clustered-Index in (DataDate, RowNumber) Bedeutet, dass neue Daten (mit vermutlich höherem DataDates als derzeit gespeichert) an das logische Ende des Clustered-Index auf neuen Seiten angehängt werden. Dadurch wird der unnötige Aufwand beim Teilen von Seiten beseitigt und die Ladezeiten verkürzt. 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 sein können.

Wenn nichts anderes, suchen Ihre Abfragen viel häufiger nach DataDate als nach RowNumber. Ein Clustered-Index für (DataDate, RowNumber) Unterstützt Indexsuchen für DataDate (und dann RowNumber). Die bestehende Anordnung unterstützt nur Suchen auf RowNumber (und nur dann vielleicht auf DataDate). Möglicherweise können Sie den vorhandenen nicht gruppierten Index auf DataDate löschen, sobald der Primärschlüssel geändert wurde. Der Clustered-Index ist breiter als der nicht Clustered-Index, den er ersetzt. Sie sollten daher testen, ob die Leistung akzeptabel bleibt.

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

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

Um die beste Leistung beim Laden von Daten zu erzielen, versuchen Sie möglicherweise, minimal protokollierte Einfügungen zu erzielen. Weitere Informationen finden Sie unter:

52
Paul White 9

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

Fragmentierung im Allgemeinen ist ein roter Hering. Die Reduzierung der Fragmentierung sollte hier nicht Ihr Ziel sein, aber eine angemessene Organisation für Ihre Abfragen sollte es sein. Eine reduzierte Fragmentierung zusätzlich zu erreichen, ist ein guter Gedanke, aber kein eigenständiges Ziel. Wenn Sie ein ordnungsgemäß organisiertes Datenmodell haben, das Ihrer Arbeitslast entspricht (Ihre Abfragen werden ordnungsgemäß abgedeckt) nd Sie haben Messungen, die zeigen, dass Fragmentierung die Leistung beeinträchtigt, dann können wir darüber sprechen.

13
Remus Rusanu