Verwenden von indizierten Ansichten für Aggregate - zu gut, um wahr zu sein?

28

Wir haben ein Data Warehouse mit einer ziemlich großen Datensatzanzahl (10-20 Millionen Zeilen) und führen häufig Abfragen aus, die Datensätze zwischen bestimmten Datumsangaben oder Datensätze mit bestimmten Flags zählen, z

SELECT
    f.IsFoo,
    COUNT(*) AS WidgetCount
FROM Widgets AS w
JOIN Flags AS f
    ON f.FlagId = w.FlagId
WHERE w.Date >= @startDate
GROUP BY f.IsFoo

Die Leistung ist nicht schlecht, kann aber relativ träge sein (vielleicht 10 Sekunden bei kaltem Cache).

Kürzlich entdeckte ich, dass ich GROUP BYin indizierten Ansichten verwenden kann, und probierte etwas Ähnliches wie das Folgende aus

CREATE VIEW TestView
WITH SCHEMABINDING
AS
    SELECT
        Date,
        FlagId,
        COUNT_BIG(*) AS WidgetCount
    FROM Widgets
    GROUP BY Date, FlagId;
GO

CREATE UNIQUE CLUSTERED INDEX PK_TestView ON TestView
(
    Date,
    FlagId
);

Infolgedessen ist die Leistung meiner ersten Abfrage jetzt <100 ms und die resultierende Ansicht und der resultierende Index <100 KB (obwohl unsere Zeilenzahl groß ist, bedeutet der Bereich von Datums- und Flag-IDs, dass diese Ansicht nur 1000 bis 2000 Zeilen enthält).

Ich dachte, dass dies vielleicht die Leistung von Schreibvorgängen in die Widget-Tabelle verdreifachen würde, aber nein - die Leistung von Einfügungen und Aktualisierungen in diese Tabelle ist meines Erachtens ziemlich unbeeinträchtigt (und da es sich um ein Data Warehouse handelt, wird diese Tabelle nur selten aktualisiert sowieso)

Für mich ist das viel zu schön, um wahr zu sein - oder? Was muss ich beachten, wenn ich indizierte Ansichten auf diese Weise verwende?

Justin
quelle
2
Können Sie Ihre Skripte so umschreiben, dass sie tatsächlich gültiges SQL sind? Ihre SELECTund CREATE VIEWSkripte sind falsch, wie ich glaube, ist Ihr CREATE INDEXSkript.
Mark Sinkinson
2
@ MarkSinkinson Entschuldigung, stellt sich heraus, dass der Versuch, gültiges SQL für imaginäre Tabellen zu schreiben, schwierig ist
Justin
Der Teil "Zu schön, um wahr zu sein" kam für mich, als ich erweiterte Ansichten haben wollte, z. B. solche, die MAX-, Self- oder Outer-Joins enthalten, oder eine Ansicht indizieren wollte, die selbst auf eine andere Ansicht verweist - was zumindest in SQL Server nicht der Fall ist erlaubt docs.microsoft.com/en-us/sql/relational-databases/views/… . So werde ich immer zu ehrgeizig und muss dann die Dinge zurückfahren. Aber für die einfacheren Aggregationen sind sie wirklich großartig - sogar SUM wird unterstützt.
Simon_Weaver

Antworten:

29

Wie Sie bereits bemerkt haben, materialisiert die Ansicht selbst nur eine geringe Anzahl von Zeilen. Selbst wenn Sie die gesamte Tabelle aktualisieren, ist der zusätzliche Aufwand für die Aktualisierung der Ansicht vernachlässigbar. Sie haben wahrscheinlich schon den größten Schmerz verspürt, den Sie spüren werden, als Sie die Ansicht erstellt haben. Die nächste Annäherung ist, wenn Sie der Basistabelle mehrere Millionen Zeilen mit einer Reihe neuer IDs hinzufügen, für die neue Zeilen in der Ansicht erforderlich sind.

Das ist nicht zu gut, um wahr zu sein. Sie verwenden indizierte Ansichten genau so, wie sie verwendet werden sollten - oder zumindest auf eine der effektivsten Arten: Sie bezahlen für zukünftige Abfrageaggregationen zum Zeitpunkt des Schreibens. Dies funktioniert am besten, wenn das Ergebnis viel kleiner als die Quelle ist und die Aggregationen natürlich häufiger angefordert werden, als die zugrunde liegenden Daten aktualisiert werden (in DW im Allgemeinen häufiger als in OLTP).

Leider halten viele Leute das Indizieren einer Ansicht für magisch - ein Index macht nicht alle Ansichten effizienter, insbesondere Ansichten, die einfach Tabellen verknüpfen und / oder die gleiche Anzahl von Zeilen wie die Quelle erzeugen (oder sogar multiplizieren). In diesen Fällen ist die E / A aus der Ansicht gleich oder sogar schlechter als die ursprüngliche Abfrage. Dies liegt nicht nur daran, dass es die gleichen oder mehr Zeilen gibt, sondern sie speichern und materialisieren häufig auch mehr Spalten. Das Materialisieren im Voraus bringt also keine Vorteile, da E / A, Netzwerk und Client-Verarbeitung / -Rendering - selbst bei SSDs - immer noch die primären Engpässe bei der Rückgabe großer Ergebnismengen an den Client darstellen. Die Einsparungen, die Sie durch das Vermeiden des Joins zur Laufzeit erzielen, sind im Vergleich zu allen anderen Ressourcen, die Sie noch verwenden, nicht messbar.

Achten Sie genau wie bei nicht gruppierten Indizes darauf, dass Sie nicht zu viel tun. Wenn Sie einer Tabelle 10 verschiedene indizierte Ansichten hinzufügen, wirkt sich dies stärker auf den Schreibteil Ihrer Workload aus, insbesondere wenn die Gruppierungsspalte (n) nicht im Clustering-Schlüssel enthalten sind.

Meine Güte, ich wollte über dieses Thema bloggen.

Aaron Bertrand
quelle
19

Aarons Antworten deckten diese Frage gut ab. Zwei Dinge, die hinzugefügt werden müssen:

  1. Durch Aggregation indizierte Ansichten können zu zeilenübergreifenden Konflikten und Deadlocks führen. Normalerweise blockieren zwei Einfügungen nicht (mit Ausnahme eher seltener Bedingungen wie Sperreneskalation oder Sperren-Hash-Kollisionen). Wenn jedoch beide Einfügungen dieselbe Gruppe in der Ansicht ansprechen, werden sie miteinander konkurrieren. Der gleiche Punkt steht für alles andere, was Sperren benötigt (DML, Sperrhinweise).
  2. Indizierte Ansichten, die nicht aggregiert werden, können ebenfalls hilfreich sein. Mit ihnen können Sie Spalten aus mehreren Tabellen indizieren. Auf diese Weise können Sie effizient nach einer Tabelle filtern und nach einer Spalte aus einer verknüpften Tabelle sortieren. Mit diesem Muster können vollständige Tabellenverknüpfungen in winzige Abfragen mit konstanter Zeit umgewandelt werden.

Ich habe sowohl Aggregation als auch Join-Ansichten mit extremem Nutzen verwendet.

Alles in allem scheint Ihr Anwendungsfall ein perfekter Fall zu sein. Indizierte Ansichten sind eine Technik, die viel zu wenig genutzt wird.

usr
quelle