Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Zwischenspeichern von temporären SQL Server-Objekten

Das Erstellen einer Tabelle ist ein relativ ressourcenintensiver und zeitaufwändiger Vorgang. Der Server muss Speicherplatz für die neuen Daten- und Indexstrukturen lokalisieren und zuweisen und die entsprechenden Einträge in mehreren Systemmetadatentabellen vornehmen. All diese Arbeiten müssen so ausgeführt werden, dass sie bei hoher Parallelität immer korrekt funktionieren und alle ACID-Garantien erfüllen, die von einer relationalen Datenbank erwartet werden.

In SQL Server bedeutet dies, die richtigen Arten von Sperren und Latches in der richtigen Reihenfolge zu verwenden und gleichzeitig sicherzustellen, dass detaillierte Transaktionsprotokolleinträge sicher in den dauerhaften Speicher übernommen werden, bevor physische Änderungen an der Datenbank vorgenommen werden. Diese Protokolleinträge stellen sicher, dass das System die Datenbank im Falle eines Transaktions-Rollbacks oder Systemabsturzes wieder in einen konsistenten Zustand bringen kann.

Das Löschen eines Tisches ist ein ähnlich teurer Vorgang. Glücklicherweise erstellen oder löschen die meisten Datenbanken Tabellen nicht sehr häufig. Die offensichtliche Ausnahme hiervon ist die Systemdatenbank tempdb . Diese einzelne Datenbank enthält den physischen Speicher, Zuordnungsstrukturen, Systemmetadaten und Transaktionsprotokolleinträge für alle temporären Tabellen und Tabellenvariablen in der gesamten SQL Server-Instanz.

Es liegt in der Natur temporärer Tabellen und Tabellenvariablen, viel häufiger erstellt und gelöscht zu werden als andere Datenbankobjekttypen. Wenn diese natürlich hohe Häufigkeit der Erstellung und Zerstörung mit dem Konzentrationseffekt aller temporären Tabellen und Tabellenvariablen kombiniert wird, die einer einzigen Datenbank zugeordnet sind, ist es kaum überraschend, dass Konflikte in den Zuordnungs- und Metadatenstrukturen der tempdb Datenbank.

Temporäres Caching von Objekten

Um die Auswirkungen auf tempdb zu reduzieren Strukturen kann SQL Server temporäre Objekte zur Wiederverwendung zwischenspeichern. Anstatt ein temporäres Objekt zu löschen, behält SQL Server die Systemmetadaten bei und schneidet die Tabellendaten ab. Wenn die Tabelle 8 MB oder kleiner ist, wird die Kürzung synchron durchgeführt; andernfalls wird verzögertes Ablegen verwendet. In beiden Fällen reduziert die Kürzung den Speicherbedarf auf eine einzelne (leere) Datenseite und die Zuordnungsinformationen auf eine einzelne IAM-Seite.

Caching vermeidet beim nächsten Mal fast alle Zuordnungs- und Metadatenkosten für die Erstellung des temporären Objekts. Als Nebeneffekt weniger Änderungen an der tempdb vorzunehmen Datenbank als ein vollständiger Drop-and-Recreate-Zyklus reduziert temporäres Objekt-Caching auch den Umfang der erforderlichen Transaktionsprotokollierung.

Caching erreichen

Sowohl Tabellenvariablen als auch lokale temporäre Tabellen können zwischengespeichert werden. Um sich für das Caching zu qualifizieren, muss eine lokale temporäre Tabelle oder Tabellenvariable müssen in einem Modul erstellt werden:

  • Gespeicherte Prozedur (einschließlich einer temporär gespeicherten Prozedur)
  • Auslöser
  • Tabellenwertfunktion mit mehreren Anweisungen
  • Skalare benutzerdefinierte Funktion

Der Rückgabewert einer Tabellenwertfunktion mit mehreren Anweisungen ist eine Tabellenvariable, die selbst zwischengespeichert werden kann. Tabellenwertparameter (die auch Tabellenvariablen sind) können zwischengespeichert werden, wenn der Parameter von einer Clientanwendung gesendet wird, beispielsweise in .NET-Code mit SqlDbType.Structured . Wenn die Anweisung parametrisiert ist, können Tabellenwert-Parameterstrukturen nur auf SQL Server 2012 oder höher zwischengespeichert werden.

Das Folgende kann nicht zwischengespeichert werden:

  • Globale temporäre Tabellen
  • Objekte, die mit Ad-hoc-SQL erstellt wurden
  • Objekte, die mit dynamischem SQL erstellt wurden (z. B. mit EXECUTE oder sys.sp_executesql )

Um zwischengespeichert zu werden, muss ein temporäres Objekt zusätzlich nicht :

  • Benannte Beschränkungen haben (Beschränkungen ohne explizite Namen sind völlig in Ordnung)
  • Führen Sie "DDL" nach der Objekterstellung durch
  • In einem Modul sein, das mit WITH RECOMPILE definiert wurde Möglichkeit
  • Mit dem WITH RECOMPILE aufgerufen werden Option des EXECUTE Erklärung

Um einige häufige Missverständnisse ausdrücklich anzusprechen:

  • TRUNCATE TABLE nicht Caching verhindern
  • DROP TABLE nicht Caching verhindern
  • UPDATE STATISTICS nicht Caching verhindern
  • Automatische Erstellung von Statistiken nicht Caching verhindern
  • Manuelle CREATE STATISTICS wird Caching verhindern

Alle temporären Objekte in einem Modul werden separat auf Caching-Eignung bewertet. Ein Modul, das ein oder mehrere temporäre Objekte enthält, die nicht zwischengespeichert werden können, kann sich dennoch für das Zwischenspeichern anderer temporärer Objekte innerhalb desselben Moduls qualifizieren.

Ein gängiges Muster, das das Caching für temporäre Tabellen deaktiviert, ist die Erstellung von Indizes nach der anfänglichen Anweisung zur Tabellenerstellung. In den meisten Fällen kann dies mithilfe von Primärschlüsseln und eindeutigen Einschränkungen umgangen werden. In SQL Server 2014 und höher haben wir die Möglichkeit, nicht eindeutige Nonclustered-Indizes mithilfe von INDEX direkt in der Tabellenerstellungsanweisung hinzuzufügen Klausel.

Überwachung und Wartung

Wir können sehen, wie viele temporäre Objekte derzeit zwischengespeichert sind, indem wir die Cache-Zähler DMV:

verwenden
SELECT
    DOMCC.[type],
    DOMCC.pages_kb,
    DOMCC.pages_in_use_kb,
    DOMCC.entries_count,
    DOMCC.entries_in_use_count
FROM sys.dm_os_memory_cache_counters AS DOMCC 
WHERE 
    DOMCC.[name] = N'Temporary Tables & Table Variables';

Ein Beispielergebnis ist:

Ein Cache-Eintrag gilt als in Verwendung solange irgendein Teil des enthaltenden Moduls ausgeführt wird. Gleichzeitige Ausführungen desselben Moduls führen dazu, dass mehrere zwischengespeicherte temporäre Objekte erstellt werden. Mehrere Ausführungspläne für dasselbe Modul (möglicherweise aufgrund unterschiedlicher Sitzungs-SET Optionen) führt auch zu mehreren Cache-Einträgen für dasselbe Modul.

Cache-Einträge können im Laufe der Zeit als Reaktion auf konkurrierende Speicheranforderungen veraltet sein. Zwischengespeicherte temporäre Objekte können auch entfernt werden (asynchron durch einen Hintergrund-System-Thread), wenn der Ausführungsplan des übergeordneten Moduls aus dem Plan-Cache entfernt wird.

Obwohl dies für Produktionssysteme nicht unterstützt (oder in irgendeiner Weise empfohlen) wird, kann der temporäre Objekt-Cache-Speicher zu Testzwecken manuell vollständig gelöscht werden mit:

DBCC FREESYSTEMCACHE('Temporary Tables & Table Variables')
    WITH MARK_IN_USE_FOR_REMOVAL;
WAITFOR DELAY '00:00:05';

Die Verzögerung von fünf Sekunden gibt Zeit für die Ausführung der Hintergrundbereinigungstask. Beachten Sie, dass dieser Befehl eigentlich gefährlich ist . Sie sollten es (auf eigenes Risiko) nur auf einer Testinstanz verwenden, auf die Sie exklusiven Zugriff haben. Wenn Sie mit dem Testen fertig sind, starten Sie die SQL Server-Instanz neu.

Caching-Implementierungsdetails

Tabellenvariablen werden durch eine 'echte' Benutzertabelle in der tempdb implementiert Datenbank (allerdings keine Tabelle, die wir direkt abfragen können). Der Name der zugeordneten Tabelle ist "#", gefolgt von der achtstelligen hexadezimalen Darstellung der Objekt-ID. Die folgende Abfrage zeigt die Beziehung:

-- A table variable
DECLARE @Z AS table (z integer NULL);
 
-- Corresponding sys.tables entry
SELECT
    T.[name],
    ObjIDFromName = CONVERT(integer, CONVERT(binary(4), RIGHT(T.[name], 8), 2)),
    T.[object_id],
    T.[type_desc],
    T.create_date,
    T.modify_date
FROM tempdb.sys.tables AS T 
WHERE
    T.[name] LIKE N'#[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]';

Ein Beispielergebnis ist unten gezeigt. Beachten Sie, wie die aus dem Objektnamen berechnete Objekt-ID mit der tatsächlichen Objekt-ID übereinstimmt:

Wenn Sie dieses Skript als Ad-hoc-SQL ausführen, wird eine andere tempdb erstellt Objekt-ID (und Objektname) bei jeder Ausführung (kein Caching). Wenn dasselbe Skript in einem Modul (z. B. einer gespeicherten Prozedur) platziert wird, kann die Tabellenvariable zwischengespeichert werden (solange kein dynamisches SQL verwendet wird), sodass die Objekt-ID und der Name bei jeder Ausführung gleich sind.

Wenn die Tabellenvariable nicht zwischengespeichert wird, wird die zugrunde liegende Tabelle jedes Mal erstellt und gelöscht. Wenn das Zwischenspeichern temporärer Objekte aktiviert ist, wird die Tabelle am Ende des Moduls abgeschnitten, anstatt gelöscht zu werden. Es gibt keine Änderungen zu Systemmetadaten, wenn eine Tabellenvariable zwischengespeichert wird. Die Auswirkungen auf Zuordnungsstrukturen und Transaktionsprotokollierung beschränken sich auf das Löschen der Zeilen in der Tabelle und das Entfernen aller überschüssigen Daten und Zuordnungsseiten, wenn das Modul endet.

Temporäre Tabellen

Wenn eine temporäre Tabelle anstelle einer Tabellenvariablen verwendet wird, ist der grundlegende Mechanismus im Wesentlichen derselbe, mit nur ein paar zusätzlichen Umbenennungsschritten:Wenn eine temporäre Tabelle nicht zwischengespeichert wird , es ist in tempdb sichtbar mit dem vertrauten, vom Benutzer bereitgestellten Namen, gefolgt von einer Reihe von Unterstrichen und der hexadezimalen Darstellung der Objekt-ID als abschließendes Suffix. Die lokale temporäre Tabelle bleibt bestehen, bis sie explizit gelöscht wird oder bis der Bereich, in dem sie erstellt wurde, endet. Für Ad-hoc-SQL bedeutet dies, wenn die Sitzung vom Server getrennt wird.

Für eine zwischengespeicherte temporäre Tabelle , wenn das Modul zum ersten Mal ausgeführt wird, wird die temporäre Tabelle genau wie für den nicht zwischengespeicherten Fall erstellt. Am Ende des Moduls wird die temporäre Tabelle nicht automatisch gelöscht (wenn der Bereich endet, in dem sie erstellt wurde), sondern abgeschnitten und dann umbenannt zur hexadezimalen Darstellung der Objekt-ID (genau wie für die Tabellenvariable). Wenn das Modul das nächste Mal ausgeführt wird, wird die zwischengespeicherte Tabelle vom Hexadezimalformat in den vom Benutzer bereitgestellten Namen umbenannt (plus Unterstriche plus Hex-Objekt-ID).

Die zusätzlichen Umbenennungsvorgänge am Anfang und am Ende des Moduls beinhalten eine kleine Anzahl von System-Metadatenänderungen . Bei zwischengespeicherten temporären Tabellen kann es daher bei sehr hohen Wiederverwendungsraten immer noch zu Metadatenkonflikten kommen. Dennoch ist die Auswirkung auf die Metadaten einer zwischengespeicherten temporären Tabelle viel geringer als im nicht zwischengespeicherten Fall (jedes Mal Erstellen und Löschen der Tabelle).

Weitere Details und Beispiele dazu, wie temporäres Objekt-Caching funktioniert, finden Sie in meinem vorherigen Artikel.

Statistiken zu zwischengespeicherten temporären Tabellen

Wie bereits erwähnt, können Statistiken automatisch erstellt werden auf temporären Tabellen erstellt, ohne die Vorteile des Zwischenspeicherns temporärer Objekte zu verlieren (zur Erinnerung, das manuelle Erstellen von Statistiken wird Caching deaktivieren).

Ein wichtiger Vorbehalt ist, dass die Statistiken die einer zwischengespeicherten temporären Tabelle zugeordnet sind, werden nicht zurückgesetzt wenn das Objekt am Ende des Moduls zwischengespeichert wird oder wenn das zwischengespeicherte Objekt zu Beginn des Moduls aus dem Cache abgerufen wird. Folglich können Statistiken zu einer zwischengespeicherten temporären Tabelle von einer nicht verwandten vorherigen Ausführung übrig bleiben. Mit anderen Worten, die Statistiken können absolut keinen Zusammenhang aufweisen zum aktuellen Inhalt der temporären Tabelle.

Dies ist offensichtlich unerwünscht, da der Hauptgrund dafür, eine lokale temporäre Tabelle einer Tabellenvariablen vorzuziehen, die Verfügbarkeit genauer Verteilungsstatistiken ist. Zur Risikominderung werden die Statistiken automatisch aktualisiert, wenn (falls) die kumulierte Anzahl von Änderungen am zugrunde liegenden zwischengespeicherten Objekt den internen Neukompilierungsschwellenwert erreicht. Dies ist im Voraus schwer zu beurteilen, da die Details komplex und etwas kontraintuitiv sind.

Die umfassendste Problemumgehung unter Beibehaltung der Vorteile des temporären Objekt-Cachings ist:

  • Manuell UPDATE STATISTICS auf der temporären Tabelle innerhalb des Moduls; und
  • Fügen Sie eine OPTION (RECOMPILE) hinzu Hinweis auf Anweisungen, die auf die temporäre Tabelle verweisen

Natürlich ist dies mit Kosten verbunden, aber dies ist meistens akzeptabel. Tatsächlich sagt der Modulautor durch die Entscheidung, überhaupt eine lokale temporäre Tabelle zu verwenden, implizit, dass die Planauswahl wahrscheinlich empfindlich auf den Inhalt der temporären Tabelle reagiert, sodass eine Neukompilierung sinnvoll sein kann. Das manuelle Aktualisieren von Statistiken stellt sicher, dass die während der Neukompilierung verwendeten Statistiken den aktuellen Inhalt der Tabelle widerspiegeln (wie wir es sicherlich erwarten würden).

Weitere Einzelheiten dazu, wie das genau funktioniert, finden Sie in meinem vorherigen Artikel zu diesem Thema.

Zusammenfassung und Empfehlungen

Das Zwischenspeichern von temporären Objekten innerhalb eines Moduls kann den Druck auf gemeinsam genutzte Zuordnungs- und Metadatenstrukturen in der tempdb erheblich reduzieren Datenbank. Die größte Reduzierung tritt bei der Verwendung von Tabellenvariablen auf, da das Zwischenspeichern und Wiederverwenden dieser temporären Objekte überhaupt keine Änderung von Metadaten erfordert (keine Umbenennungsvorgänge). Konflikte bei Zuordnungsstrukturen können immer noch auftreten, wenn die einzelne gecachte Datenseite nicht ausreicht, um alle Daten der Tabellenvariablen zur Laufzeit zu speichern.

Die Auswirkungen auf die Planqualität aufgrund fehlender Kardinalitätsinformationen für Tabellenvariablen können durch die Verwendung von OPTION(RECOMPILE) gemildert werden oder Ablaufverfolgungsflag 2453 (verfügbar ab SQL Server 2012). Beachten Sie, dass diese Maßnahmen dem Optimierer nur Informationen über die Gesamtzahl der Zeilen in der Tabelle liefern.

Um zu verallgemeinern, Tabellenvariablen werden am besten verwendet, wenn die Daten klein sind (idealerweise in eine einzelne Datenseite passen, um maximale Wettbewerbsvorteile zu erzielen) und wenn die Planauswahl nicht von den in der Tabellenvariablen vorhandenen Werten abhängt.

Wenn Informationen über die Datenverteilung (Dichte und Histogramme) für die Planauswahl wichtig ist, verwenden Sie eine lokale temporäre Tabelle stattdessen. Stellen Sie sicher, dass Sie die Bedingungen für das Zwischenspeichern temporärer Tabellen erfüllen, was meistens bedeutet, dass nach der ursprünglichen Anweisung zur Tabellenerstellung keine Indizes oder Statistiken erstellt werden. Dies wird ab SQL Server 2014 durch die Einführung des INDEX komfortabler -Klausel von CREATE TABLE Erklärung.

Eine explizite UPDATE STATISTICS nachdem Daten in die temporäre Tabelle geladen wurden, und OPTION (RECOMPILE) Hinweise auf Anweisungen, die auf die Tabelle verweisen, können erforderlich sein, um alle erwarteten Vorteile von zwischengespeicherten temporären Tabellen innerhalb eines Moduls zu erzielen.

Wichtig ist, temporäre Objekte nur dann einzusetzen, wenn sie einen klaren Nutzen bringen, meistens in Bezug auf die Planqualität. Übermäßiger, ineffizienter oder unnötiger Gebrauch von temporären Objekten kann zu tempdb führen Konflikt, auch wenn temporäres Objekt-Caching erreicht wird.

Optimales Zwischenspeichern temporärer Objekte reicht möglicherweise nicht aus, um tempdb zu reduzieren Konflikt in allen Fällen auf ein akzeptables Niveau, selbst wenn temporäre Objekte nur verwendet werden, wenn dies vollständig gerechtfertigt ist. Die Verwendung von In-Memory-Tabellenvariablen oder nicht dauerhaften In-Memory-Tabellen kann in solchen Fällen gezielte Lösungen bieten, obwohl immer Kompromisse eingegangen werden müssen und keine einzelne Lösung derzeit die beste Option für alle Fälle darstellt.