Database
 sql >> Datenbank >  >> RDS >> Database

Reflexleistungsoptimierung:Falsche Verwendung von temporären Tabellen

In dieser Fortsetzung meiner Serie zur Leistungsoptimierung möchte ich vier häufige Probleme erörtern, die ich bei der Verwendung temporärer Tabellen sehe. Jedes dieser Probleme kann eine Arbeitslast lahmlegen, es lohnt sich also, sie in Ihrer Umgebung zu kennen und zu suchen.

Problem 1:Temporäre Tabellen verwenden, wo sie nicht benötigt werden

https://www.flickr. com/photos/tea_time/3890677277/

Temporäre Tabellen haben eine Vielzahl von Verwendungszwecken (wahrscheinlich ist die häufigste das Speichern einer Zwischenergebnismenge für die spätere Verwendung), aber Sie müssen bedenken, dass Sie beim Einfügen einer temporären Tabelle in eine Abfrage den Datenfluss durch die Abfrageprozessor.

Stellen Sie sich das Auffüllen einer temporären Tabelle als einen harten Stopp vor, da es eine Abfrage (nennen wir es den Produzenten) gibt, um die Zwischenergebnismenge zu erzeugen, die dann in der temporären Tabelle in tempdb gespeichert wird, und dann die nächste Abfrage (nennen wir es der Consumer) muss die Daten erneut aus der temporären Tabelle lesen.

Ich habe oft festgestellt, dass einige Teile einer Arbeitslast tatsächlich eine bessere Leistung erbringen, wenn die temporäre Tabelle vollständig entfernt wird, sodass die Daten vom Erzeugerteil der Abfrage zum Verbraucherteil der Abfrage fließen, ohne dass sie in tempdb gespeichert werden müssen, und die Der Abfrageoptimierer kann einen optimaleren Gesamtplan erstellen.

Sie denken jetzt vielleicht:„Warum sollte jemand eine temporäre Tabelle verwenden, wenn sie die Dinge langsamer macht?“ - Und das zu Recht! In solchen Fällen habe ich festgestellt, dass die Verwendung einer temporären Tabelle im Entwicklungsteam institutionalisiert wurde; Jemand hat vor vielen Jahren festgestellt, dass die Verwendung einer temporären Tabelle die Leistung erhöht, sodass temporäre Tabellen zur Standardauswahl für das Design wurden.

Dies kann schwer zu ändern sein, besonders wenn Sie einen leitenden Entwickler oder Manager haben, der davon überzeugt ist, dass immer temporäre Tabellen verwendet werden sollten. Versuchen Sie es einfach mit einer teuren Abfrage (z. B. einer mit langer Laufzeit oder einer, die viele Male pro Sekunde ausgeführt wird) und entfernen Sie eine oder mehrere der temporären Tabellen, um zu sehen, ob die Leistung ohne sie steigt. Und wenn ja, gibt es Ihren Beweis für die Unnachgiebigen!

Problem 2:Fehlende Filterung beim Füllen temporärer Tabellen

Auch wenn Sie eine temporäre Tabelle nicht entfernen können, können Sie die Leistung möglicherweise drastisch verbessern, indem Sie sicherstellen, dass der Code, der die temporäre Tabelle füllt, die aus den Quelltabellen gezogenen Daten korrekt filtert.

Ich habe aufgehört zu zählen, wie oft ich gesehen habe, wie eine temporäre Tabelle mit Code gefüllt wurde, der mit SELECT * beginnt , enthält ein paar uneingeschränkte Joins und hat keine WHERE-Klausel, und die spätere Abfrage, die die temporäre Tabelle verwendet, verwendet nur wenige Spalten und hat eine WHERE-Klausel, um die Anzahl der Zeilen stark zu reduzieren.

Ich erinnere mich an einen Fall, in dem eine temporäre Tabelle in einer gespeicherten Prozedur Daten aus 15 Jahren aus der Hauptdatenbank aggregierte und dann nur die Daten des aktuellen Jahres verwendet wurden. Dies führte wiederholt dazu, dass tempdb anwuchs, bis der Speicherplatz auf dem Datenträger erschöpft war und die gespeicherte Prozedur dann fehlschlug.

Wenn Sie eine temporäre Tabelle füllen, verwenden Sie nur die erforderlichen Quelltabellenspalten und nur die erforderlichen Zeilen – d. h. schieben Sie die Filterprädikate nach oben in den Füllcode der temporären Tabelle. Dies spart nicht nur Platz in tempdb, sondern auch viel Zeit, da nicht benötigte Daten aus der Quelltabelle kopiert werden müssen (und möglicherweise die Notwendigkeit entfällt, Quelldatenbankseiten überhaupt von der Festplatte zu lesen).

Problem 3:Falsche Indexierung temporärer Tabellen

Genau wie bei normalen Tabellen sollten Sie nur die Indizes erstellen, die tatsächlich vom späteren Abfragecode verwendet werden, um die Abfrageleistung zu verbessern. Ich habe viele Fälle gesehen, in denen es einen Nonclustered-Index pro temporärer Tabellenspalte gibt und Einzelspalten-Indizes, die ohne Analyse des späteren Codes ausgewählt werden, oft ziemlich nutzlos sind. Kombinieren Sie jetzt nutzlose Nonclustered-Indizes mit einem Mangel an Filtern beim Füllen der temporären Tabelle, und Sie haben ein Rezept für ein enormes Aufblähen von tempdb.

Außerdem ist es im Allgemeinen schneller, die Indizes zu erstellen, nachdem die Tabelle gefüllt wurde. Dies gibt den zusätzlichen Vorteil, dass die Indizes über genaue Statistiken verfügen, was die Abfrage weiter unterstützen kann, da der Abfrageoptimierer in der Lage sein wird, eine genaue Kardinalitätsschätzung vorzunehmen.

Eine Menge nicht gruppierter Indizes zu haben, die nicht verwendet werden, verschwendet nicht nur Speicherplatz, sondern auch die Zeit, die benötigt wird, um sie zu erstellen. Wenn dies in häufig ausgeführtem Code der Fall ist, kann das Entfernen dieser nicht benötigten Indizes, die bei jeder Ausführung des Codes erstellt werden, erhebliche Auswirkungen auf die Gesamtleistung haben.

Problem 4:tempdb-Latch-Konflikt

Es ist durchaus üblich, dass es in tempdb einen Engpass gibt, der auf die Verwendung temporärer Tabellen zurückgeführt werden kann. Wenn es viele gleichzeitige Verbindungen gibt, die Code ausführen, der temporäre Tabellen erstellt und löscht, kann der Zugriff auf die Zuweisungs-Bitmaps der Datenbank im Speicher zu einem erheblichen Engpass werden.

Dies liegt daran, dass jeweils nur ein Thread eine Zuweisungs-Bitmap ändern kann, um Seiten (aus der temporären Tabelle) als zugewiesen oder freigegeben zu markieren, und alle anderen Threads warten müssen, wodurch der Workload-Durchsatz verringert wird. Obwohl es seit SQL Server 2005 einen Cache für temporäre Tabellen gibt, ist dieser nicht sehr groß, und es gibt Einschränkungen, wann die temporäre Tabelle zwischengespeichert werden kann (z. B. nur, wenn sie weniger als 8 MB groß ist).

Herkömmliche Methoden zur Umgehung dieses Problems bestanden darin, das Ablaufverfolgungsflag 1118 und mehrere tempdb-Datendateien zu verwenden (weitere Informationen finden Sie in diesem Blogbeitrag), aber eine andere zu berücksichtigende Sache ist, die temporären Tabellen vollständig zu entfernen!

Zusammenfassung

Temporäre Tabellen können sehr nützlich sein, aber sie werden sehr leicht und häufig falsch verwendet. Wenn Sie Code schreiben (oder überprüfen), der eine temporäre Tabelle verwendet, sollten Sie Folgendes beachten:

  • Wird diese temporäre Tabelle wirklich benötigt ?
  • Ist der Code, der die Tabelle unter Verwendung der richtigen Filterung füllt um die temporäre Tabellengröße zu begrenzen?
  • Werden Indizes nach der Tabellenauffüllung erstellt (allgemein) und sind die verwendeten Indizes durch späteren Code?

Paul White hat ein paar großartige Posts (hier und hier) über temporäre Objektverwendung und Caching, die ich ebenfalls zum Lesen empfehle.

Und noch eine letzte Sache, wenn Sie sich entscheiden, keine temporäre Tabelle zu verwenden, ersetzen Sie sie nicht einfach durch eine Tabellenvariable, einen allgemeinen Tabellenausdruck oder einen Cursor (alles übliche Methoden, mit denen Leute versuchen, die temporäre Tabelle) – finden Sie heraus, wie Sie den Code am effizientesten (neu) schreiben – es gibt keine allgemeingültige Antwort.

Bis zum nächsten Mal, viel Spaß bei der Fehlersuche!