LINQ Joins - Leistung

8

Ich bin gespannt, wie genau LINQ (nicht LINQ to SQL) ausführt, hinter den Kulissen in Bezug darauf, wie Sql Server Joins durchführt.

Sql Server generiert vor Ausführung einer Abfrage einen Ausführungsplan. Der Ausführungsplan ist im Grunde ein Ausdrucksbaum, von dem er glaubt, dass er die Abfrage am besten ausführt. Jeder Knoten enthält Informationen darüber, ob eine Sortierung, ein Scan, eine Auswahl, eine Verknüpfung usw. durchgeführt werden soll.

Auf einem 'Join'-Knoten in unserem Ausführungsplan können wir drei mögliche Algorithmen sehen; Hash Join, Merge Join und Nested Loops Join. Sql Server wählt den Algorithmus für jede Join-Operation basierend auf der erwarteten Anzahl von Zeilen in inneren und äußeren Tabellen, welche Art von Join wir durchführen (einige Algorithmen unterstützen nicht alle Arten von Joins), ob wir Daten geordnet benötigen und wahrscheinlich viele andere Faktoren.

Join Algorithmen:

Verschachtelte Schleifenverbindung:    Am besten für kleine Eingaben, kann mit geordneter innerer Tabelle optimiert werden.

Zusammenführungs-Join:    Am besten für mittlere bis große Eingaben sortierte Eingaben oder eine Ausgabe, die bestellt werden muss.

Hash-Verknüpfung:    Am besten für mittlere bis große Eingänge, kann linear skaliert werden.

LINQ-Abfrage:

%Vor%

SQL-Abfrage:

%Vor%

Sql Server könnte einen Nested Loop Join verwenden, wenn er weiß, dass es eine kleine Anzahl von Zeilen aus jeder Tabelle gibt, einen Merge Join, wenn er weiß, dass eine der Tabellen einen Index hat, und Hash, wenn er weiß, dass es viele gibt Zeilen auf beiden Tabellen und keiner hat einen Index.

Wählt Linq seinen Algorithmus für Joins? oder benutzt es immer einen?

    
Meiscooldude 14.06.2010, 15:03
quelle

3 Antworten

3

Linq to SQL sendet keine Verknüpfungshinweise an den Server. Daher ist die Leistung eines Joins mit Linq to SQL identisch mit der Leistung desselben Joins, der "direkt" an den Server gesendet wird (d. H. Mit reinem ADO oder SQL Server Management Studio), ohne dass irgendwelche Hinweise angegeben wurden.

Linq to SQL erlaubt auch nicht Ihnen Join-Hinweise zu verwenden (soweit ich weiß). Wenn Sie also einen bestimmten Join-Typ erzwingen wollen, müssen Sie eine gespeicherte Prozedur oder die Execute[Command|Query] -Methode verwenden. Wenn Sie jedoch keinen Join-Typ angeben, indem Sie INNER [HASH|LOOP|MERGE] JOIN schreiben, wählt SQL Server immer den Join-Typ aus, von dem er denkt, dass er am effizientesten ist - es spielt keine Rolle, woher die Abfrage stammt.

Andere Linq-Abfrageanbieter - wie Entity Framework und NHibernate Linq - werden genau das Gleiche tun wie Linq to SQL. Keiner von diesen hat direkte Kenntnisse darüber, wie Sie Ihre Datenbank indiziert haben, und daher sendet keiner von ihnen Join-Hinweise.

Linq to Objects ist etwas anders - es wird (fast?) immer einen "Hash-Join" im SQL Server-Sprachgebrauch durchführen. Das liegt daran, dass die für einen Merge-Join erforderlichen Indizes nicht vorhanden sind und Hash-Joins normalerweise effizienter sind als verschachtelte Schleifen, sofern die Anzahl der Elemente nicht sehr klein ist. Aber die Anzahl der Elemente in IEnumerable<T> zu bestimmen, kann von vornherein eine vollständige Iteration erfordern. In den meisten Fällen ist es also schneller, einfach das Schlimmste anzunehmen und einen Hashing-Algorithmus zu verwenden.

    
Aaronaught 14.06.2010, 15:07
quelle
6

Die Methoden in System.Linq.Enumerable werden in der Reihenfolge ausgeführt, in der sie ausgegeben werden. Es gibt keinen Abfrageoptimierer beim Spielen.

Viele Methoden sind sehr faul, sodass Sie die Quelle nicht vollständig aufzählen können, indem Sie am Ende der Abfrage .First oder .Any oder .Take setzen. Das ist die einfachste Optimierung, die es zu haben gilt.

Speziell für System.Linq.Enumerable.Join die Dokumente , dass dies ein a Hash-Verknüpfung.

  

Der standardmäßige Gleichheitsvergleich, Standard, wird zum Hashing und Vergleichen von Schlüsseln verwendet.

Also Beispiele:

%Vor%     
Amy B 14.06.2010 16:02
quelle
1

LINQ selbst wählt keine Algorithmen irgendeiner Art aus, da LINQ streng genommen nur eine Möglichkeit darstellt, eine Abfrage in SQL-artiger Syntax auszudrücken, die Funktionsaufrufen auf IEnumerable<T> oder IQueryable<T> zugeordnet werden kann. LINQ ist vollständig eine Sprachfunktion und bietet keine Funktionalität, sondern nur eine andere Möglichkeit, bestehende Funktionsaufrufe auszudrücken.

Im Fall von IQueryable<T> ist es dem Anbieter (wie LINQ to SQL) überlassen, die beste Methode zum Erstellen der Ergebnisse auszuwählen.

Im Falle von LINQ to Objects (mit IEnumerable<T> ) wird in allen Fällen eine einfache Enumeration verwendet (entspricht ungefähr verschachtelten Schleifen). Es gibt keine tiefere Inspektion (oder sogar Kenntnis) der zugrunde liegenden Datentypen, um die Abfrage zu optimieren.

    
Adam Robinson 14.06.2010 15:07
quelle

Tags und Links