Vielen Dank, dass Sie den Abfrageplan hinzugefügt haben. es ist sehr informativ. Ich habe eine Reihe von Empfehlungen, die auf dem Abfrageplan basieren, aber zunächst eine Einschränkung: Nehmen Sie nicht nur das, was ich sage, und nehmen Sie an, dass es korrekt ist, probieren Sie es zuerst aus (idealerweise in Ihrer Testumgebung) und stellen Sie sicher, dass Sie verstehen, warum die Änderungen funktionieren oder verbessern Sie Ihre Anfrage nicht!
Der Abfrageplan: eine Übersicht
Aus diesem Abfrageplan (sowie dem entsprechenden XML) können wir sofort einige nützliche Informationen ersehen:
- Sie befinden sich in SQL 2012
- Dies ist eine klassische Star-Join-Abfrage, und Sie profitieren von der In-Row-Bitmap-Filteroptimierung , die in SQL 2008 für solche Pläne hinzugefügt wurde
- Die Faktentabelle enthält ungefähr 1,5 Milliarden Zeilen, und etwas mehr als 500 Millionen dieser Zeilen stimmen mit den Dimensionsfiltern überein
- Die Abfrage fordert 72 GB Arbeitsspeicher an, erhält jedoch nur 12 GB Arbeitsspeicher (vermutlich sind 12 GB das Maximum, das für eine bestimmte Abfrage gewährt wird, was bedeutet, dass Ihr Computer wahrscheinlich über ~ 64 GB Arbeitsspeicher verfügt).
- SQL Server führt ein Sortierstromaggregat durch, das 500 Millionen Zeilen auf nur 600.000 Zeilen reduziert. Die Sortierung überschreitet die Speicherzuweisung und wird auf Tempdb übertragen
- Wir haben Warnungen für planbeeinflussende Konvertierungen aufgrund expliziter und impliziter Konvertierungen in Ihrer Abfrage
- Die Abfrage verwendet 32 Threads, aber die anfängliche Suche in Ihrer Faktentabelle weist einen enormen Thread-Versatz auf. Nur 2 der 32 Threads erledigen die ganze Arbeit. (In den folgenden Schritten des Abfrageplans ist die Arbeit jedoch ausgewogener.)
Optimierung: Columnstore oder nicht
Dies ist eine schwierige Frage, aber insgesamt würde ich Columnstore in diesem Fall nicht empfehlen. Der Hauptgrund ist, dass Sie sich in SQL 2012 befinden. Wenn Sie also ein Upgrade auf SQL 2014 durchführen können, lohnt es sich möglicherweise, Columnstore auszuprobieren.
Im Allgemeinen ist Ihre Abfrage der Typ, für den der Spaltenspeicher entwickelt wurde, und kann von der reduzierten E / A des Spaltenspeichers und der höheren CPU-Effizienz des Stapelmodus erheblich profitieren.
Die Einschränkungen des Spaltenspeichers in SQL 2012 sind jedoch einfach zu groß, und das Tempdb-Überlaufverhalten , bei dem SQL Server durch einen Überlauf den Batch-Modus vollständig aufgibt , kann eine verheerende Strafe sein, die bei den großen Zeilenmengen auftreten kann arbeiten mit. Wenn Sie sich für Columnstore unter SQL 2012 entscheiden, sollten Sie darauf vorbereitet sein, alle Ihre Abfragen sehr genau zu bearbeiten und sicherzustellen, dass der Stapelmodus immer verwendet werden kann.
Optimierung: mehr Partitionen?
Ich denke nicht, dass mehr Partitionen dieser speziellen Abfrage helfen werden. Sie können es natürlich gerne ausprobieren, aber denken Sie daran, dass die Partitionierung in erster Linie eine Datenverwaltungsfunktion ist (die Möglichkeit, neue Daten in Ihren ETL-Prozessen über SWITCH PARTITION
und keine Leistungsfunktion auszutauschen. In einigen Fällen kann dies natürlich die Leistung verbessern.) In ähnlicher Weise kann dies jedoch die Leistung anderer beeinträchtigen (z. B. viele Singleton-Suchvorgänge, die jetzt einmal pro Partition ausgeführt werden müssen).
Wenn Sie sich für Columnstore entscheiden, ist das Laden Ihrer Daten zur besseren Segmenteliminierung meiner Meinung nach wichtiger als das Partitionieren. Idealerweise möchten Sie wahrscheinlich so viele Zeilen in jeder Partition wie möglich, um vollständige Spaltenspeichersegmente und hohe Komprimierungsraten zu erhalten.
Optimierung: Verbesserung der Kardinalitätsschätzungen
Da Sie eine große Faktentabelle und eine Handvoll sehr kleiner (Hunderte oder Tausende von Zeilen) Zeilen aus jeder Dimensionstabelle haben, würde ich einen Ansatz empfehlen, bei dem Sie explizit eine temporäre Tabelle erstellen, die nur die Dimensionszeilen enthält, die Sie verwenden möchten . Anstatt sich beispielsweise Dim_Date
einer komplizierten Logik anzuschließen cast(right(ALHDWH.dwh.Dim_Date.Financial_Year,4) as int) IN ( 2015, 2014, 2013, 2012, 2011 )
, sollten Sie eine Abfrage vor der Verarbeitung schreiben, um nur die Zeilen zu extrahieren, die Ihnen wichtig sind, Dim_Date
und diesen Zeilen die entsprechende PK hinzuzufügen.
Auf diese Weise kann SQL Server Statistiken nur für die Zeilen erstellen, die Sie tatsächlich verwenden. Dies kann zu besseren Kardinalitätsschätzungen im gesamten Plan führen. Da diese Vorverarbeitung im Vergleich zur Gesamtkomplexität der Abfragen einen so geringen Arbeitsaufwand darstellt, würde ich diese Option wärmstens empfehlen.
Optimierung: Reduzierung des Fadenversatzes
Es ist wahrscheinlich, dass das Extrahieren der Daten aus Dim_Date
einer eigenen Tabelle und das Hinzufügen eines Primärschlüssels zu dieser Tabelle auch dazu beitragen würde, den Thread-Versatz zu verringern (ein Ungleichgewicht der Arbeit zwischen den Threads). Hier ist ein Bild, das zeigt, warum:
In diesem Fall enthält die Dim_Date
Tabelle 22.000 Zeilen. SQL Server schätzt, dass Sie 7.700 dieser Zeilen verwenden werden, und Sie haben tatsächlich nur 1.827 dieser Zeilen verwendet.
Da SQL Server Statistiken verwendet, um Threads Zeilenbereiche zuzuweisen, sind die schlechten Kardinalitätsschätzungen in diesem Fall wahrscheinlich die Hauptursache für die sehr schlechte Verteilung der Zeilen.
Thread-Versatz in 1.872 Zeilen mag nicht viel ausmachen, aber der schmerzliche Punkt ist, dass dies dann zur Suche in Ihrer 1,5-Milliarden-Zeilen-Faktentabelle führt, in der 30 Threads im Leerlauf sitzen, während 600 Millionen Zeilen von 2 Threads verarbeitet werden.
Optimierung: Beseitigung der Art Verschüttung
Ein weiterer Bereich, auf den ich mich konzentrieren würde, ist die Sortierung. Ich denke, dass das Hauptproblem in diesem Fall schlechte Kardinalitätsschätzungen sind. Wie wir unten sehen können, geht SQL Server davon aus, dass die Gruppierungsoperation, die durch die Kombination von a Sort
und ausgeführt Stream Aggregate
wird, 324 Millionen Zeilen ergibt. Es ergibt jedoch nur 643.000 Zeilen.
Wenn SQL Server wüsste, dass so wenige Zeilen aus dieser Gruppierung hervorgehen würden, würde es mit ziemlicher Sicherheit ein HASH GROUP
(Hash-Aggregat) anstelle eines SORT GROUP
(Sort-Stream) verwenden, um Ihre GROUP BY
Klausel zu implementieren .
Es ist möglich, dass sich dies von selbst behebt, wenn Sie einige der anderen oben genannten Änderungen vornehmen, um die Kardinalitätsschätzungen zu verbessern. Wenn dies nicht der Fall ist, können Sie versuchen, den OPTION (HASH GROUP)
Abfragehinweis zu verwenden, um SQL Server dazu zu zwingen. Auf diese Weise können Sie das Ausmaß der Verbesserung bewerten und entscheiden, ob Sie den Abfragehinweis in der Produktion verwenden möchten oder nicht. Bei Abfragehinweisen bin ich im Allgemeinen vorsichtig, aber das Festlegen von "Nur" HASH GROUP
ist viel einfacher als das Verwenden eines Verknüpfungshinweises, das Verwenden FORCE ORDER
oder anderweitige Entfernen eines zu großen Teils der Kontrolle aus den Händen des Abfrageoptimierers.
Optimierung: Speichergewährung
Ein letztes potenzielles Problem war, dass SQL Server schätzte, dass die Abfrage 72 GB Speicher verwenden möchte, Ihr Server jedoch nicht so viel Speicher für die Abfrage bereitstellen konnte. Obwohl es technisch gesehen richtig ist, dass das Hinzufügen von mehr Speicher zum Server hilfreich ist, gibt es meines Erachtens mindestens ein paar andere Möglichkeiten, um dieses Problem anzugehen:
- Entfernen Sie den
Sort
Operator (wie oben beschrieben); Es ist wirklich der einzige Operator, der in Ihrer Abfrage einen erheblichen Speicherbedarf verbraucht
- Teilen Sie Ihre Anfrage in mehrere Stapel auf. Es kann beispielsweise vorkommen, dass Sie die Abfrage einmal pro Partition ausführen können. Dies könnte die Größe der Sortierung verringern, sie im Speicher behalten und möglicherweise die Leistung erheblich verbessern. Ein Nebeneffekt könnte sein, dass Sie Threads möglicherweise besser nutzen, wenn Sie nur auf eine Partition zugreifen, da dies in einigen Fällen die Art und Weise beeinflusst, wie SQL Server Partitionen Threads zuweist.