Wie viele Partitionen sollte ich für meine Clustered Columnstore-Indextabellen erstellen? Sollte ich die Rowstore-Tabellen auch partitionieren?

7

Ich habe ein Data Warehouse, das aus vier Clustered Columnstore-Indextabellen (CCI) und neun Rowstore-Tabellen besteht. Diese Tabellen werden nur für Analysen verwendet und die CCI-Daten werden alle 15 Minuten aus Staging-Tabellen eingefügt. Ich möchte die Abfrageleistung durch Hinzufügen von Partitionen und Sortieren optimieren.

Alle Abfragen dieser Daten basieren auf einem ganzzahligen Feld mit etwa 350 unterschiedlichen Werten. Die CCI ganz links verfügt über 100 Millionen Datensätze und 125 Spalten. Es gibt drei untergeordnete CCIs mit demselben Ganzzahlfeld. CCI 2 hat 15 Millionen Datensätze und 150 Spalten, CCI 3 und 4 haben jeweils etwa 30 Millionen Datensätze und jeweils 25 Spalten.

Von diesen 350 verschiedenen Ganzzahlen ist die Verteilung der Datensatzanzahl in der Tabelle ganz links wie folgt:

  • 5% größer als 1M
  • 46% größer als 100.000
  • 83% größer als 10K

Darüber hinaus gibt es neun weitere Rowstore-Tabellen, die ebenfalls mit den CCIs verbunden sind. Diese haben Rieseleinfügungen, sind untergeordnete Elemente der CCIs und enthalten alle dasselbe ganzzahlige Feld. Diese Zeilenspeicher haben ähnliche oder kleinere Datensatzvolumina, jeweils <10 Spalten, zwei enthalten LOBS und zwei werden häufig massenweise aktualisiert (diese Aktualisierungen basieren auch auf dem ID-Feld).

Wie viele Partitionen soll ich machen?

Sollte ich die Rowstore-Tabellen auch partitionieren?

Gibt es wichtige Überlegungen, die ich übersehen habe?

Hinweis zur "Sortierung", die ich bereits erwähnt habe:

Ein Datumsfeld in der CCI ganz links ist in diesen Abfragen häufig ein sekundäres Prädikat. Daher möchte ich diese CCI etwa alle vier Wochen nach Datum sortieren, um sie zu warten. Ich werde diese Sortierung erreichen, indem ich die CCI lösche, am Datum einen Clustered Rowstore-Index hinzufüge, diesen Index lösche und dann die CCI mit MAXDOP = 1 erneut hinzufüge. Ich möchte auch die untergeordneten CCIs nach dem Join-Schlüssel für ihre Eltern sortieren.

Cyndi Baker
quelle

Antworten:

8

Vorteile der Partitionierung einer IHK:

  1. Die Abfrageleistung kann verbessert werden, da ein Mindestmaß an Zeilengruppeneliminierung gewährleistet ist, unabhängig davon, wie die Daten geladen oder geändert werden. Die meisten allgemeinen Richtlinien zur SQL Server-Partitionierung berücksichtigen dies nicht.

  2. Verbesserte Flexibilität bei Wartungsvorgängen, da Sie auf Partitionsebene Neuerstellungen oder auf Partitionsebene Reorgs durchführen können (nach dem Ausschalten der Partition). Sie können auch verschiedene Partitionen an verschiedene Dateigruppen senden, aber ich muss Sie darauf hinweisen, dass dies die Leistung fast nie verbessert. Dateigruppen sind eine Wartungsfunktion. Das Erhöhen der Dateizahl kann manchmal die Leistung verbessern. Abhängig von Ihrem Speicher-Setup möchten Sie mit ziemlicher Sicherheit, dass die für Ihre Abfragen relevanten Daten auf mehrere Dateien verteilt werden, um die E / A zu verbessern.

  3. Die Partitionseliminierung umfasst mehr Szenarien als die Zeilengruppeneliminierung in derselben Spalte. Beispielsweise ist ein Filter von WHERE ID < 0 OR ID > 10nicht für die Eliminierung von Zeilengruppen geeignet, qualifiziert sich jedoch für die Eliminierung von Partitionen.

  4. Das Schleifen nach Partitionen kann hilfreich sein, wenn Wartungsvorgänge ausgeführt werden, bei denen alle Zeilen geändert werden müssen. Angenommen, Sie fügen einer Tabelle eine neue Spalte hinzu, die aus vorhandenen Spalten in dieser Tabelle abgeleitet werden kann. Durch die Partitionierung können Sie diese Arbeit bei Bedarf effizient in Stapel aufteilen.

Nachteile der Partitionierung einer CCI:

  1. Ohne Wartung kann die Anzahl der Zeilen in Delta-Zeilengruppen drastisch zunehmen. Stellen Sie sich eine nicht partitionierte CCI vor, die bei MAXDOP 8 mit parallelen Einfügungen geladen ist. Im Delta-Speicher befinden sich höchstens 4194304 Zeilen. Wenn die Tabelle auf 50 Partitionen geändert wird, können jetzt 209715200 Zeilen im Delta-Speicher gespeichert werden.

  2. Abfragepläne für Einfügungen und Löschungen in den Spaltenspeicher können einen Sortieroperator als untergeordnetes Element des DML-Operators enthalten. Wenn diese Art nicht genügend Speicher erhält, kann dies zu extremen Leistungseinbußen führen. Ich empfehle, jeweils nur eine Partition zu ändern, wenn Sie paralleles Einfügen verwenden.

  3. Wenn Sie Ihre Partitionsfunktion unklug wählen, können zu kleine Partitionen entstehen. Viele Leute werden Sie auf das Zeilenlimit von 1048576 für eine Zeilengruppe als ideale Größe hinweisen, aber ich persönlich halte die Vorteile einer Anreise für übertrieben. Sie möchten wahrscheinlich viele kleine Partitionen vermeiden, wenn Sie es trotzdem helfen können.

  4. Wenn Sie zu viele Partitionen in Ihrer Tabelle oder Ihrer Datenbank haben, können schlimme Dinge passieren. Leider ist dies nicht sehr genau definiert und es ist schwierig, eine glaubwürdige Quelle für das zu finden, was "zu viele Partitionen" tatsächlich bedeuten. Ich habe von Problemen mit den Kompilierungszeiten für Abfragen gehört und gesehen. Auch hier gab es kürzlich eine AntwortDBCC CHECKTABLE .

Wenden Sie das Obige auf Ihr Szenario an: Mit der Anzahl der Zeilen, die Sie haben, sollten Sie auf keinen der wirklich schlimmen Fälle stoßen. Für die Abfrageleistung benötigen einige Benutzer sehr schnelle Ausführungszeiten für Abfragen und müssen so viele Zeilengruppen wie möglich überspringen. Andere benötigen nur ein Mindestmaß an Zeilengruppeneliminierung, da der größte Teil der in der Abfrage geleisteten Arbeit außerhalb der Spaltenspeicher-Scans liegt. Das macht es für jemanden von außen schwierig, Ihnen eine Empfehlung für die Anzahl der Partitionen zu geben. Für die 100-Millionen-Tabelle könnte alles von 4 bis 100 vernünftig sein.

Sie können versuchen, einige Ihrer Abfragen mit einer unterschiedlichen Anzahl von Zeilen in den Partitionen zu testen, um festzustellen, wie sich die Leistung ändert. Dies kann simuliert werden, indem Kopien der Tabellen erstellt werden oder indem eine Partitionsfunktion für eine Tabelle mit absichtlicher Schiefe erstellt und die ID geändert wird, nach der Sie filtern. Wenn Sie die Ergebnisse einer ausreichend guten Abfrageleistung verwenden und sicherstellen, dass beim Laden von Daten keine Probleme auftreten, sollten Sie gut sein.

Die Rowstores sind für die Frage nicht relevant, oder vielmehr eine ganz andere Frage. Partitionierung ist nicht das richtige Tool, um die Leistung bei Rowstore-Abfragen zu verbessern. Ich habe Leistungssteigerungen auf Systemen gesehen, indem ich Columnstore-Tabellen partitioniert und die Rowstore-Tabellen in Ruhe gelassen habe.

Joe Obbish
quelle
6

Update nach Partitionierung bis zur Produktion:

Die Entscheidung für die richtige Partitionierung für einen Clustered Columnstore Index (CCI) ist ein sehr maßgeschneiderter Prozess. Wenn die falschen Partitionen ausgewählt werden, sind Leistung und Komprimierung schlechter als in einem nicht partitionierten Schema.

Da ich vier CCIs partitioniert habe, habe ich die CCI mit den wenigsten Datensätzen ausgewählt und ihre Datensatzanzahl durch 1.048.576 (die ideale CCI-Zeilengruppengröße) geteilt. Ich habe diesen Quotienten als meine vorgeschlagene Anzahl von Partitionen verwendet. Dann habe ich Abfragen zur Datensatzanzahl basierend auf diesem Schema ausgeführt, um die tatsächlichen Zeilenzahlen pro Partition zurückzugeben. Dieser Schritt sollte sicherstellen, dass die Datensätze einigermaßen gleichmäßig auf die Partitionen verteilt sind. Dort war. Ich Glückspilz.

Ein Hindernis trat auf: Dieser Produktionsanalyseprozess half mir, die richtige Anzahl von Partitionen zu ermitteln, jedoch nur für die Produktion. Meine unteren Umgebungen sind viel kleiner als die Produktion. Die gewählte Partitionierungsstufe hat die Daten so fein geschnitten, dass ich überhaupt keine vollständigen Zeilengruppen hatte. Die unteren Datenbanken wurden größer und die Abfragezeiten blieben gleich. IO ging dramatisch zurück und ich musste wiederholt darauf hinweisen, als die Gewinne dieser Initiative in Frage gestellt wurden. Es war schwer zu beweisen, dass die Partitionierung wirklich helfen würde, bis ich zur Produktion kam.

Das Ergebnis: Die Partitionierung war ein großer Erfolg in der Produktion. IO ist weit unten und meine Abfragezeiten wurden um 70% oder mehr reduziert. Ich habe auch mehr Optionen zum Verwalten dieser Tabellen in kleinen Blöcken.

Einige Hinweise: Wählen Sie das richtige Feld für die Partitionierung aus. Wenn Sie Abfragen haben, die in vielen Partitionen navigieren müssen, kann dies zu einer Leistungsminderung führen. Außerdem habe ich Raum für Wachstum gelassen und meiner Partitionsfunktion Partitionen und Bereiche für Daten hinzugefügt, die jetzt nicht vorhanden sind, aber eines Tages verfügbar sein werden.

Ursprüngliche Antwort nur von lokalen Tests:

Seit ich diese Frage gestellt habe, habe ich mehr Forschung und einen POC vor Ort betrieben. Es wurde vorgeschlagen, diesen POC in einer Antwort zu teilen.

In meinem POC habe ich eine Partitionsfunktion verwendet von:

CREATE PARTITION FUNCTION [MyIntPF](int) 
AS RANGE LEFT 
FOR VALUES (
    N'50'
    , N'100'
    , N'150'
    , N'200'
    , N'250'
    , N'300'
    , N'350'
    , N'400'
    , N'450'
    , N'500'
);

CREATE PARTITION SCHEME [MyIntPS] 
AS PARTITION [MyIntPF] 
TO (
    [MyInt050fg]
    , [MyInt100fg]
    , [MyInt150fg]
    , [MyInt200fg]
    , [MyInt250fg]
    , [MyInt300fg]
    , [MyInt350fg]
    , [MyInt400fg]
    , [MyInt450fg]
    , [MyInt500fg]
    , [MyInt000fg]
);

Diese Funktion weist jeder Partition 50 MyInts zu, die Platz für ein wenig Wachstum bieten.

Denken Sie daran, dass ich ungefähr 350 verschiedene MyInts in den 170 Millionen Datensätzen in den PROD-CCIs habe. David Browne schlug eine Mindestdatensatzgröße von 1 MB in einer Partition vor, die sinnvoll ist, um ein CCI-komprimiertes Segment zu optimieren. Ich irre mich aus zwei Gründen. Der erste Grund ist, die Erstellung eines POC-Monsters mit 100 Partitionen zu vermeiden. Das zweite ist, dass ich davon ausgehe, dass 1M für jede Tabelle in der Partition gilt. Ich partitioniere vier Spaltenspeicher, von denen der kleinste 25 Millionen Datensätze hat. Wenn ich es in 100 Teile zerbrechen würde, würde es niemals ein vollständiges Segment erreichen.

In meiner lokalen Entwicklungsdatenbank habe ich 2,2 Millionen Datensätze in der CCI ganz links und noch weniger als in den untergeordneten CCIs. Dies stellt ein Problem für die Erstellung einer realistischen Replikation von PROD dar. Ich sollte wirklich ein wenig zusätzliche Zeit priorisieren, um eine große lokale Datenbank dafür zu erstellen, aber in der Zwischenzeit sind hier die Vorher / Nachher-E / A-Ergebnisse der lokalen Partition. Ich habe nach einem Aggregat von meiner CCI ganz links gefragt, das auf MyInt = einem einzelnen Wert basiert.

Nicht partitioniert

Scananzahl 1, logische Lesevorgänge 0, physische Lesevorgänge 0, Vorlesevorgänge 0, logische Lesevorgänge 1548, 
lob physische liest 0, lob liest voraus liest 44.
Segment liest 4, Segment übersprungen 0.

Partitioniert

Scananzahl 1, logische Lesevorgänge 0, physische Lesevorgänge 0, Vorauslesevorgänge 0, logische Lesevorgänge 268, 
lob physische liest 0, lob liest voraus liest 0.
Segment liest 1, Segment übersprungen 0.

Wie erwartet konnte SQL Server alle Partitionen bis auf eine in einer Abfrage mit einem MyInt-Gleichheitsprädikat überspringen.

Ich arbeite weiter daran und sollte Zeit haben, mich hier zu aktualisieren, wenn die Dinge voranschreiten.

Cyndi Baker
quelle