Hat der Spool-Iterator im ersten Plan einen plausiblen Nutzen?
Dies hängt davon ab, was Sie als "plausibel" betrachten, aber die Antwort gemäß dem Kostenmodell lautet "Ja". Dies ist natürlich richtig, da der Optimierer immer den günstigsten Plan auswählt, den er findet.
Die eigentliche Frage ist, warum das Kostenmodell den Plan mit der Spule für so viel billiger hält als den Plan ohne. Berücksichtigen Sie geschätzte Pläne, die für eine neue Tabelle (aus Ihrem Skript) erstellt wurden, bevor dem Delta-Speicher Zeilen hinzugefügt wurden:
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE);
Die geschätzten Kosten für diesen Plan betragen 771.734 Einheiten :
Die Kosten hängen fast ausschließlich mit dem Löschen des Clustered Index zusammen, da erwartet wird, dass die Löschvorgänge zu einer großen Anzahl zufälliger E / A-Vorgänge führen. Dies ist nur die generische Logik, die für alle Datenänderungen gilt. Beispielsweise wird angenommen, dass ein ungeordneter Satz von Modifikationen an einem B-Tree-Index zu weitgehend zufälligen E / A mit damit verbundenen hohen E / A-Kosten führt.
Datenänderungspläne können aus genau diesen Kostengründen eine Sortierung enthalten, um die Zeilen in einer Reihenfolge darzustellen, die den sequenziellen Zugriff fördert. Die Auswirkung wird in diesem Fall verstärkt, da die Tabelle partitioniert ist. Tatsächlich sehr unterteilt; Ihr Skript erstellt 15.000 davon. Zufällige Aktualisierungen einer sehr partitionierten Tabelle sind besonders kostspielig, da auch der Preis für das Wechseln von Partitionen (Rowsets) während des Streams hoch ist.
Der letzte wichtige Faktor, der berücksichtigt werden muss, ist, dass die oben genannte einfache Aktualisierungsabfrage (wobei "Aktualisierung" jede Datenänderungsoperation, einschließlich eines Löschvorgangs, bedeutet) für eine Optimierung mit der Bezeichnung "Rowset-Sharing" qualifiziert ist, bei der für das Scannen und das gleiche interne Rowset verwendet wird Aktualisieren der Tabelle. Der Ausführungsplan enthält weiterhin zwei separate Operatoren, es wird jedoch nur ein Rowset verwendet.
Ich erwähne dies, weil die Möglichkeit, diese Optimierung anzuwenden, bedeutet, dass der Optimierer einen Codepfad verwendet, der die potenziellen Vorteile einer expliziten Sortierung zur Reduzierung der Kosten für zufällige E / A einfach nicht berücksichtigt . Wenn es sich bei der Tabelle um einen B-Baum handelt, ist dies sinnvoll, da die Struktur inhärent geordnet ist und die gemeinsame Nutzung des Rowsets alle potenziellen Vorteile automatisch bietet.
Die wichtige Konsequenz ist, dass die Kalkulationslogik für den Aktualisierungsoperator diesen Bestellvorteil (Förderung von sequenziellen E / A oder anderen Optimierungen) nicht berücksichtigt, wenn das zugrunde liegende Objekt ein Spaltenspeicher ist. Dies liegt daran, dass Änderungen am Spaltenspeicher nicht direkt durchgeführt werden. Sie nutzen einen Delta Store. Das Kostenmodell spiegelt daher einen Unterschied zwischen Aktualisierungen für gemeinsam genutzte Rowsets in B-Bäumen und in Spaltenspeichern wider.
In dem speziellen Fall eines (sehr!) Partitionierten Spaltenspeichers kann es dennoch von Vorteil sein, die Reihenfolge beizubehalten, da das Ausführen aller Aktualisierungen einer Partition vor dem Übergang zur nächsten aus E / A-Sicht weiterhin vorteilhaft sein kann .
Die Standardkostenlogik wird hier für Spaltenspeicher wiederverwendet, sodass ein Plan, der die Partitionsreihenfolge beibehält (jedoch nicht die Reihenfolge innerhalb jeder Partition), kostengünstiger ist. Wir können dies in der Testabfrage sehen, indem wir das undokumentierte Ablaufverfolgungsflag 2332 verwenden, um eine sortierte Eingabe für den Aktualisierungsoperator zu erfordern. Dies setzt die DMLRequestSort
Eigenschaft bei der Aktualisierung auf true und zwingt das Optimierungsprogramm, einen Plan zu erstellen, der alle Zeilen für eine Partition bereitstellt, bevor zum nächsten übergegangen wird:
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332);
Die geschätzten Kosten für diesen Plan sind mit 52.5174 Einheiten sehr viel niedriger :
Diese Kostensenkung ist allesamt auf die niedrigeren geschätzten E / A-Kosten bei der Aktualisierung zurückzuführen. Der eingeführte Spool führt keine nützliche Funktion aus, außer dass er die Ausgabe in der Partitionsreihenfolge garantieren kann, wie dies durch das Update mit erforderlich ist DMLRequestSort = true
(der serielle Scan eines Spaltenspeicherindex kann diese Garantie nicht bieten). Die Kosten der Spule selbst werden als relativ niedrig angesehen, insbesondere im Vergleich zu der (wahrscheinlich unrealistischen) Kostensenkung bei der Aktualisierung.
Die Entscheidung, ob eine geordnete Eingabe an den Aktualisierungsoperator erforderlich ist, wird sehr früh bei der Abfrageoptimierung getroffen. Die in dieser Entscheidung verwendeten Heuristiken wurden nie dokumentiert, können jedoch durch Ausprobieren ermittelt werden. Es scheint, dass die Größe von Delta-Speichern ein Input für diese Entscheidung ist. Einmal getroffen, ist die Auswahl für die Abfragezusammenstellung permanent. Kein USE PLAN
Hinweis wird erfolgreich sein: Das Ziel des Plans hat entweder Eingaben für das Update angeordnet oder nicht.
Es gibt eine andere Möglichkeit, einen kostengünstigen Plan für diese Abfrage zu erhalten, ohne die Kardinalitätsschätzung künstlich einzuschränken. Eine ausreichend niedrige Schätzung, um den Spool zu vermeiden, führt wahrscheinlich dazu, dass DMLRequestSort falsch ist, was aufgrund der erwarteten zufälligen E / A zu sehr hohen geschätzten Planungskosten führt. Eine Alternative besteht darin, das Ablaufverfolgungsflag 8649 (paralleler Plan) in Verbindung mit 2332 (DMLRequestSort = true) zu verwenden:
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332, QUERYTRACEON 8649);
Dies führt zu einem Plan, der einen parallelen Scan pro Partition im Batch-Modus und einen ordnungserhaltenden (zusammengeführten) Austausch von Gather-Streams verwendet:
Abhängig von der Laufzeiteffektivität der Partitionsreihenfolge auf Ihrer Hardware kann dies die beste Leistung bringen. Allerdings sind große Änderungen keine gute Idee für den Spaltenspeicher, sodass die Idee des Partitionswechsels mit ziemlicher Sicherheit besser ist. Wenn Sie mit den langen Kompilierungszeiten und den ungewöhnlichen Planoptionen fertig werden, die häufig bei partitionierten Objekten zu finden sind - insbesondere, wenn die Anzahl der Partitionen groß ist.
Die Kombination vieler, relativ neuer Features, insbesondere in der Nähe ihrer Grenzen, ist eine gute Möglichkeit, schlechte Ausführungspläne zu erhalten. Die Tiefe der Optimiererunterstützung nimmt im Laufe der Zeit tendenziell zu, aber die Verwendung von 15.000 Partitionen des Spaltenspeichers bedeutet wahrscheinlich immer, dass Sie in interessanten Zeiten leben.
OPTION (QUERYRULEOFF EnforceHPandAccCard)
verschwindet die Spule. Ich gehe davon aus, dass HP "Halloween Protection" ist. Der Versuch, diesen Plan mit einemUSE PLAN
Hinweis zu verwenden, schlägt jedoch fehl (ebenso wie der Versuch, den Plan aus derOPTIMIZE FOR
Problemumgehung zu verwenden)