Partitionieren / Indizieren einer extrem großen Tabelle

7

Ich arbeite an der Indizierung und Partitionierung einer einzelnen Data Warehouse-Tabelle mit einem Gewicht von ca. 500 GB. Die Tabelle ist ein Heap, hat über hundert TEXTSpalten und die TEXT_IN_ROWOption ist aktiviert. Ich habe diese Tabelle nicht entworfen und kann sie in naher Zukunft nicht mehr ändern.

Ich wurde beauftragt, es zu partitionieren. Wir lösen dies mithilfe einer Kopie der Datenbank auf einem Testserver. Es kann ungefähr 2 GB pro Sekunde auf die SSD-RAID-Arrays übertragen, sodass E / A kein wesentlicher Engpass ist und 16 Kerne (2 NUMA-Knoten) und 64 GB RAM hat.

Mein Ansatz besteht darin, alle nicht gruppierten Indizes zu deaktivieren, eine Partitionsfunktion und ein Partitionsschema zu erstellen (ungefähr 12 Partitionen, alle in der PRIMARYDateigruppe - sie verwenden dies, um die fortlaufende Wartung zu ermöglichen und lokalere Einfügungen für die nächtliche ETL bereitzustellen, und nicht um I zu verteilen / O), und erstellen Sie dann mithilfe dieses Partitionsschemas einen Clustered-Index für die Tabelle.

Ich erstelle den Clustered-Index und partitioniere die Tabelle wie folgt:

CREATE CLUSTERED INDEX CX_DailyTable ON DailyTable (LoadDate, SeqNumber) 
  WITH (SORT_IN_TEMPDB = ON) ON monthly_on_primary (LoadDate)

Offensichtlich dauert es lange (3 Stunden bis zu diesem Beitrag), und ich erwarte nicht, dass es schnell geht. Was mich ein wenig beunruhigt, ist, dass Tempdb jetzt fast 1 TB drückt und stetig steigt, obwohl der aktuelle Tisch ungefähr halb so groß ist. Die MS-Dokumente, die ich gelesen habe, schlagen vor, dass die Verwendung des temporären Speicherplatzes ungefähr der Größe der endgültigen Tabelle / des Clustered-Index entsprechen sollte.

http://msdn.microsoft.com/en-us/library/ms188281.aspx

Wenn SORT_IN_TEMPDB auf ON gesetzt ist, muss in tempdb genügend freier Speicherplatz zum Speichern der Sortierläufe und ausreichend freier Speicherplatz in der Zieldateigruppe zum Speichern der endgültigen Indexstruktur vorhanden sein. Die Sortierläufe enthalten die Blattzeilen des Index.

Sind ihre Schätzungen falsch? Wird Tempdb für wesentlich mehr als nur die Sortierläufe verwendet? Oder verdoppelt das Erstellen dieses Clustered-Index irgendwie die Größe der Tabelle? (Scheint ziemlich unwahrscheinlich; es ist eine ziemlich breite Tabelle, und ich schätze, wir erhalten zusätzliche 4-8 Bytes pro Zeile plus Nicht-Blattseiten durch Hinzufügen eines Clustered-Index.)

db2
quelle
Was ist die durchschnittliche Größe einer Reihe? Bei Heaps werden die Einfügungen in die Seite eingefügt, auf die sie passen (erfolgt durch Scannen der PFS-Byte-Map). Bei gruppierten Indizes gibt es genau eine Stelle, an der die Zeile enden kann. Abhängig von Ihrer Datenverteilung kann dies einen Teil des Unterschieds ausmachen .
StrayCatDBA
1
@StrayCatDBA "Die Tabelle ist ein Heap, hat über hundert TEXT-Spalten und die Option TEXT_IN_ROW ist aktiviert. Ich habe diese Tabelle nicht entworfen und kann sie in naher Zukunft nicht mehr ändern." tempdbweint, ganz zu schweigen von den
Seitenteilen

Antworten:

16

Mein Ansatz besteht darin, alle [...] nicht gruppierten Indizes zu deaktivieren und dann mithilfe dieses Partitionsschemas einen gruppierten Index für die Tabelle zu erstellen.

Durch das Erstellen eines Clustered-Index auf einem Heap werden automatisch alle nicht gruppierten Indizes (auch deaktivierte) neu erstellt. Die nicht gruppierten Indizes werden neu erstellt, aber nicht partitioniert . Unter der Annahme, dass der gewünschte Endzustand eine partitionierte Clustertabelle mit ausgerichteten Indizes ist, ist das Wiederherstellen der nicht gruppierten Indizes, um nicht ausgerichtet zu werden, völlig verschwendeter Aufwand.

Was mich ein wenig beunruhigt, ist, dass Tempdb jetzt fast 1 TB drückt und stetig steigt, obwohl der aktuelle Tisch ungefähr halb so groß ist. Die MS-Dokumente, die ich gelesen habe, schlagen vor, dass die Verwendung des temporären Speicherplatzes ungefähr der Größe der endgültigen Tabelle / des Clustered-Index entsprechen sollte.

Die Frage des Sortierraums ist sehr komplex. Um alle Details (einschließlich der Auswirkungen der Parallelität) zu verstehen, müssen Sie eine ganze Reihe von Beiträgen des SQL Server-Abfrageverarbeitungsteams sorgfältig lesen . Das Konvertieren eines Heaps in eine partitionierte Clustertabelle mit aktivierter Parallelität ist wahrscheinlich dem schlimmsten Fall ziemlich nahe.

Im einfachsten Fall (unter Vernachlässigung der meisten wichtigen Informationen in den Beiträgen des QP-Teams) fordern Sie SQL Server auf, eine Abfrage wie die folgende auszuführen:

SELECT *
FROM DailyTable
ORDER BY
    $partition.monthly_on_primary(LoadDate),
    LoadDate,
    SeqNumber;

Diese Abfrage wird nicht schnell ausgeführt, unabhängig davon, wo Sie die Sortierläufe schreiben, in die der Speicher nicht passt. Hinzu kommt die Arbeit, eine vollständige neue Kopie des gesamten Datensatzes in separaten Rowsets zu erstellen, und die Arbeit, die mit der sinnlosen Neuerstellung der nicht gruppierten Indizes verbunden ist ...

Rat

Es gibt viele Überlegungen, wie diese Änderung effizient funktionieren kann. Die wichtigsten sind, das Sortieren nach Möglichkeit zu vermeiden und wo immer möglich eine parallele, minimal protokollierte Massenlast zu verwenden.

Die Details davon hängen von Details ab, die nicht in der Frage enthalten sind, und eine vollständige Lösung kann hier nicht beantwortet werden. Der Umriss eines Ansatzes, der in der Vergangenheit für mich persönlich gut funktioniert hat, lautet jedoch:

  • Extrahieren Sie die vorhandenen Daten mit bcpeiner Datei pro endgültiger Partition
  • Löschen Sie die vorhandene Tabelle und erstellen Sie die neue
  • Laden Sie die neue Tabelle mit paralleler, minimal protokollierter Massenlast

Der Datenextrakt pro Partition muss am bestellt werden (LoadDate, SeqNumber). Im Idealfall würden Sie einen Sortiervorgang vermeiden. Wenn für (LoadDate, SeqNumber) ein nicht gruppierter Index vorhanden ist, können Sie Daten in der richtigen Reihenfolge extrahieren, ohne sie zu sortieren, wenn Sie die Abfrage korrekt erstellen.

Sobald die Daten pro Partition in separate Dateien extrahiert wurden (dies kann parallel erfolgen, wenn Ihre Hardware dies zulässt), kann die Quelltabelle gelöscht werden, wodurch Speicherplatz frei wird. Anschließend wird ein neuer partitionierter Heap oder eine neue gruppierte Tabelle erstellt und mit den vorsortierten Daten, möglicherweise auch parallel, in großen Mengen geladen.

Richtig gemacht, erfordert der gesamte Prozess nicht mehr als das 1-fache der Datengröße und erzielt die schnellstmöglichen Datenübertragungsraten in beide Richtungen bei geringstem Protokollverbrauch.

Paul White 9
quelle
AH-HA. Da ist mein Problem. Ich wusste nicht, dass durch die Neuerstellung eines Clustered-Index automatisch alle nicht-Clustered-Indizes wieder aktiviert werden. Die gute Nachricht ist, dass es in 14 Stunden noch fertig ist. Dazu gehören der 500-GB-Heap sowie weitere 500 GB nicht gruppierter Indizes. Ich werde die Prozedur so ändern, dass die nicht gruppierten Indizes sofort gelöscht werden. Ich muss sie trotzdem ausschreiben, um sie in das Partitionsschema zu verschieben, damit dies den Prozess nicht wesentlich beeinflusst (abgesehen davon, dass es erheblich beschleunigt wird). Vielen Dank.
db2