Posted on Jul 31, 2006 von in SQL Server

Einer der effizientesten Wege, um die JOIN Performance zu erhöhen ist die Anzahl der Zeilen die geJOINt werden, zu beschränken. Dies gilt insbesondere für die äußere(n) Tabelle(n) eines JOINs. Es sollten nur die Zeilen zurückgegeben werden, die auch für den JOIN verwendet werden.

*****

Werden regelmäßig 2 oder mehr Tabellen per JOIN verbunden, kann man die Performance optimieren, indem jede der Spalten, die im JOIN verwendet werden, einen Index hat. Dies bedeutet idR die Erstellung eines Index für diese Spalten. Ganz allgemein gesprochen ist ein Clustered Index besser als ein Nonclustered für Optimum JOIN Performance.

*****

Werden regelmäßig 2 oder mehr Tabellen per JOIN verbunden, sollten also die verbindenden Spalten einen entsprechenden Index haben. Ist dieser Index nicht natürlich kompakt, kann man darüber nachdenken einen künstlichen Schlüssel zu den Tabellen hinzuzufügen, der kompakt ist, um die der Schlüssel zu verringern. Dies verringert die Lese I/O Operationen, die während der Verarbeitung des JOINs erfolgen müssen und erhöht die allgemeine Performance.

*****

JOIN Performance hat eine Menge zu tun mit der Anzahl der Zeilen, die in eine Datenseite gepackt werden können. Angenommen, es sollen 2 Tabellen geJOINed werden. Höchstwahrscheinlich wird eine dieser Tabellen kleiner als die andere sein und SQL Server wird höchstwahrscheinlich diese kleinere Tabelle als die innere Tabelle eines JOINs wählen. Wenn dies geschieht, versucht SQL Server den relevanten Inhalt dieser Tabelle in den Buffer Cache zu packen, um die Geschwindigkeit zu erhöhen. Ist dort aber nicht genügend Platz, um sämtliche relevanten Daten aufzunehmen, muß SQL Server zusätzliche Resourcen bereitstellen, um die Daten in den Cache und wieder rauszuschaffen, während der JOIN verarbeitet wird.

Wenn sämtliche relevanten Daten gecached werden können, wird der JOIN schneller sein, als wenn dies nicht der Fall ist. Damit schlägt man wieder die Brücke zum 1. Statement hier, daß die Anzahl der Zeilen die JOIN Performance beeinflussen kann. Mit anderen Worten ist es wahrscheinlicher, alle relevanten Daten der inneren Tabelle in den Cache zu bekommen und damit die Performance zu erhöhen, wenn die Tabellen keinen Platz verschwenden. Moral der Geschichte ist also, zu versuchen, soviele Zeilen wie möglich auf eine Datenseite zu bekommen. Dies kann zum Beispiel eines hohen Füllfaktors, regelmäßige Neuerstellung der Indizes, um leeren Speicherplatz freizugeben und optimierte Datentypen nud Datentyplängen bei der Tabellenerstellung erfolgen.

*****

Man sollte nicht vergessen, daß SQL Server nicht automatisch einen Index erstellt, wenn ein Fremdschlüssel erstellt wird. Plant man aber eine Tabelle mit einer anderen über diesen Fremdschlüssel zu JOINen, sollte man einen solchen Index für die Spalte(n) des Fremdschlüssels erstellen. Dies kann nachhaltig die Performance vieler JOINs erhöhen.

*****

Falls möglich, sollte man es vermeiden, Tabellen zu JOINen über Spalten, die nur wenige eindeutige Werten enthalten. Enthalten die Spalten, die im JOIN verwendet werden, kaum eindeutige Werte, ist der Optimierer des SQL Servers unter Umständen nicht in der Lage, einen existierenden Index zu verwenden, um die Verarbeitung des JOINs zu beschleunigen. Idealtypisch für die beste Performance, wäre ein JOIN über Spalten für die ein UNIQUE Index existiert.

*****

Für eine optimale JOIN Performance sollten die Datentypen der Spalten, die im JOIN verwendet werden, nummerische Datentypen sein, und nicht CHAR oder VARCHAR oder noch andere nicht-nummerische Datentypen. Der Overhead ist geringer und die JOIN Verarbeitung schneller.

*****

Um eine optimale Geschwindigkeit beim JOINen zweier Tabellen zu erreichen, sollten die Spalten, die im JOIN verwendet werden, den gleichen Datentypen und die gleiche Datentyplänge besitzen.

Dies bedeutet auch, daß man nicht Unicode mit Nicht-Unicode Spalten im JOIN mischen sollte. Wenn SQL Server implizit Datentypen konvertieren muß, um den JOIN verarbeiten zu können, beeinträchtigt dies nicht nur negativ die Performance, sondern kann unter Umständen auch bedeuten, daß SQL Server einen evtl. vorhandenen Index nicht verwenden kann, und stattdessen einen Scan dürchführen muß.

*****

Falls JOINs, die augenblicklich Hints enthalten, langsam sind, sollte man einmal die Hints entfernen, um zu überprüfen, ob nicht der Optimierer einen besseren Job bei der Optimierung eines JOINs macht als man selber. Dies gilt besonders für Upgrades von 7.0 auf 2000.

*****

Einer der einfachsten und effektivsten Wege, um JOINs zu beschleunigen, ist es sicherzustellen, daß die geJOINten Tabellen eine WHERE Klausel enthalten, um die Anzahl der Zeilen, die verarbeitet werden müssen, zu minimieren.

Einen einfachen JOIN über zwei Tabellen durchzuführen, ist nun wirklich nicht ungewöhnlich. Problem hingegen ist es aber, wenn die beteiligten Tabellen Millionen von Zeilen beinhalten. Hier sollte man unbedingt darauf achten, daß entsprechend restriktive WHERE Klauseln eingebaut werden, um die Gesamtzahl an Zeilen zu reduzieren. Dieser einfache, und logische Schritt kann nachhaltig die Performance eines JOINs zweier großer Tabellen verbessern.

*****

In einem SELECT Statement, daß einen JOIN beinhaltet, sollte man niemals * verwenden, um alle Spalten zurückzugeben. Es gibt eine ganze REihe von Gründen, warum dies schlecht ist. Erstens sollte man nur die Spalten zurückgeben, an denen man auch interessiert ist. In den seltensten Fällen ist man an allen Spalten interessiert. Je weniger Daten zurückgegeben werden müssen, umso schneller wird die Abfrage im allgemeinen sein. Zweitens gibt man die Spalte über die der JOIN läuft gleich doppelt zurück, was dazu führt, das noch mehr Daten als notwendig zurückgegeben werden, was sich wiederum negativ auf die Performance auswirkt.

Man vergegenwärtige sich einmal die folgenden beiden Abfragen:

 

USE Northwind
SELECT *
FROM Orders
INNER JOIN [Order Details]
ON Orders.OrderID = [Order Details].OrderID

und

 

USE Northwind
SELECT Orders.OrderID, Orders.OrderDate,
[Order Details].UnitPrice, [Order Details].Quantity,
[Order Details].Discount
FROM Orders
INNER JOIN [Order Details]
ON Orders.OrderID = [Order Details].OrderID

Beide Abfragen erfüllen im Grunde das Gleiche zurück. Problem bei der ersten Abfrage jedoch ist, daß sie nicht nur zu viele Spalten zurückgibt, sondern auch gleich 2x die OrderID, was überhaupt keinen Mehrwert bringt. Beide Umstände tragen dazu bei, daß unnötiger Overhead auf dem Server produziert wird, was die Performance beeinträchtigt.


*****

Während eine hohe Indexselektivität allgemein ein wichtiger Faktor ist, den der Optimierer benutzt, um festzustellen, ob ein Index verwendet werden soll oder nicht, gibt es einen besonderen Fall, für den Indizes mit einer geringen Selektivität hilfreich sein können, um SQL Server zu beschleunigen. Dies ist der Fall für Indizes auf FOREIGN KEYS. Egal, ob ein Index auf einem FOREIGN KEY eine hohe oder geringe Selektivität hat, kann dieser Index durch den Optimierer verwenden, um einen MERGE JOIN auf den beteiligten Tabellen durchzuführen. Bei einem MERGE JOIN wird eine Zeile von jeder Tabelle genommen und verglichen, ob sie der JOIN Bedingung entspricht. Haben die Tabellen die entsprechenden Indizes, kann ein MERGE JOIN durchgeführt werden, der oftmals deutlich schneller ist, als ein JOIN auf eine Tabelle mit einem FOREIGN KEY, die keinen angemessenen Index besitzt.

*****

CROSS JOINs sollten nur dann verwendet werden, wenn es keinen anderen Weg gibt, um das gewünschte Resultset zu erreichen. Unerfahrene Entwickler JOINen oftmals zwei Tabellen durch einen CROSS JOIN und benutzen anschließend DISTINCT oder GROUP BY, um das gewünschte Resultset zu erhalten. Wie man sich unschwer vorstellen kann, ist dies eine enorme Verschwendung von Serverresourcen.

*****

Hat man die Wahl zwischen einem JOIN oder einer Subquery, um einen Task zu erledigen, ist im allgemeinen der JOIN (oftmals ein OUTER JOIN) schneller als die Subquery. Doch auch hier gibt es Ausnahmen. Ist, zum Beispiel, die zurückgegebene Ergebnismenge relativ klein oder existiert kein Index auf den geJOINten Spalten, kann unter Umständen die Subquery schneller sein.

Der einzige Weg, um das herauszufinden ist, wie immer, beide Methoden auszuprobieren und die Ausführungspläne zu vergleichen. Gerade wenn dieser Code häufig ausgeführt werden soll, geht kein Weg daran vorbei, um die effektivere Methode zu ermitteln.

*****

Falls man eine Abfrage mit vieln JOINs hat, ist eine Alternative zu Denormalisierung der Tabellen die Verwendung von Indexed Views, um die Geschwindigkeit zu erhöhen. Ein Indexed View, der nur in der Enterprise Edition automatisch vom Optimierer berücksichtigt wird, ermöglicht einem die Erstellung eines Views, der aber tatsächlich ein physikalisches Objekt mit einem eigenen Clustered Index ist. Jedesmal wenn eine der Basistabellen des Views aktualisiert wird, wird auch der View aktualisiert. Die geht natürlich zu Lasten der INSERT, UPDATE und DELETE Performance der Basistabellen und man sollte sorgfältig testen, um die Vor- und Nachteile vergleichen zu können, die die Verwendung eines Indexed Views mit sich bringt

*****

Falls man eine Abfrage mit einem LEFT JOIN hat, sollte man sorgfältig testen, ob dies tatsächlich der JOIN Typ ist, den man verwenden will. Ein LEFT JOIN wird verwendet, um eine Ergebnismenge zu produzieren, in der alle Spalten der linken Tabelle des JOINs enthalten sind und nicht nur die, die der JOIN Bedingung entsprechen. Zusätzlich enthält die Ergebnismenge einen NULL Marker, falls ein Wert aus der linken Tabellen keinen passenden Wert in der rechten Tabelle besitzt. Nur wenn dies auch wirklich beabsichtigt ist, sollte man diese Art von JOIN verwenden.

In der Alltagswelt wird eigentlich recht selten eine LEFT JOIN tatsächlich benötigt. Viele Entwickler verwenden sie jedoch fälschlicherweise. Auch wenn man so vielleicht seine korrekte Ergebnismenge erhält, hat man aber unter Umständen deutlich mehr Daten zurückgegeben als unbedingt notwendig, und unnötigen Overhead verursacht.

*****

Falls man Schwierigkeiten hat, eine langsam laufende Abfrage zu tunen, die einen oder mehrere JOINs verwendet, sollte man den Ausführungsplan untersuchen, ob nicht der Optimierer evtl. einen HASH JOIN verwendet. Diese Art von JOINs wird häufig verwendet, wenn die geJOINten Tabellen nicht über geeignete Indizes verfügen.

Ein HASH JOIN ist sehr resourcenintensiv (besonders CPU und I/O) und kann die Performance eines JOINs in den Keller ziehen. Wird diese Abfrage häufig ausgeführt, sollte man dringend darüber nachdenken, geeignete Indizes zu erstellen. Danach sollte der Optimierer sehr wahrscheinlich die Indizes im JOIN verwenden und einen NESTED LOOP JOIN ausführen anstelle des HASH JOINs. Dies sollte die Performance verbessern.

*****

Dieser Eintrag wurde eingetragen von und ist abgelegt unter SQL Server. Tags: , ,

Noch kein Feedback


Formular wird geladen...