7 Mythen über Linq to Database

    Linq erschien im Jahr 2007, der erste IQueryable-Anbieter erschien ebenfalls - Linq2SQL, es funktionierte nur mit MS SQL Server, es verlangsamte sich ziemlich und deckte weit entfernt alle Szenarien ab. Fast sieben Jahre sind vergangen, mehrere Linq-Anbieter sind erschienen, die mit verschiedenen DBMS arbeiten, sie haben fast alle „Kinderkrankheiten“ der Technologie besiegt, und Linq to Database (ein generischer Name für beliebte Anbieter) ist seit einigen Jahren für den industriellen Einsatz bereit.

    Trotzdem verwendet nicht jeder Linq to Database, und dies erklärt sich nicht nur aus der Tatsache, dass das Projekt alt ist und es ziemlich schwierig ist, es in linq umzuschreiben, sondern sie bringen auch verschiedene Mythen als Argumente mit. Diese Mythen wandern von einem Unternehmen zum anderen und verbreiten sich oft über das Internet.

    In diesem Beitrag habe ich die beliebtesten Mythen und Widerlegungen zu ihnen gesammelt.


    Mythos Nummer 1


    Die Datenbank wird von einem speziell geschulten DBA verwaltet, der alle Anforderungen ausführt, und Programmierer schreiben Code, sodass Linq to Database nicht benötigt wird.

    Trotz der Anziehungskraft des Mythos funktioniert dieser Ansatz normalerweise nicht. Um effektive Abfragen durchführen zu können, muss der DBA sehr gut verstehen, was im Programm vor sich geht und welche Daten in den einzelnen Szenarien benötigt werden.

    Wenn der DBA nicht über solche Kenntnisse verfügt, ist dies in der Regel darauf zurückzuführen, dass der DBA für jede Entität einen kleinen Satz CRUD-Speicher + mehrere Speicher für die "umfangreichsten" Abfragen erstellt. Und der Rest wird bereits von den Programmierern im Code erledigt. Dies funktioniert meistens ineffizient, da im Durchschnitt viel mehr Daten abgerufen werden, als für ein bestimmtes Szenario erforderlich sind. Und es ist schwierig zu optimieren.

    Wenn der DBA jedes Szenario kennt, hat er zwei Möglichkeiten:
    a) Machen Sie viel Speicher (fast identisch), jeweils für ein bestimmtes Szenario, und dann ist es schmerzhaft, sie zu unterstützen.
    b) Erstellen Sie mehrere universelle Speicher mit einer Reihe von Parametern, in denen Sie Zeichenfolgen für optimale Abfragen anbringen. Darüber hinaus wird das Hinzufügen eines zusätzlichen Parameters zur Anforderung zu einem äußerst schwierigen Prozess.

    Beide Optionen für DBA sind sehr komplex, sodass es sich meistens um eine Hybridversion mit mehreren sehr komplexen Speicherdateien handelt und alles andere eine alltägliche CRUD ist. Mit Linq können Sie das gleiche Stitching von Zeichenfolgen viel effizienter ausführen, um optimale Abfragen im Programmcode oder nahezu optimale Abfragen zu generieren.

    Der DBA kann Ansichten und Funktionen erstellen, die in Abfragen aus dem Anwendungscode sowie in gespeicherten Prozeduren für die Stapelverarbeitung verwendet werden. Das Abfragedesign sollte jedoch der Anwendungsseite überlassen werden.

    Mythos Nummer 2


    Linq generiert ineffiziente SQL-Abfragen.

    Ein sehr oft wiederholter Mythos. Der größte Teil der Ineffizienz von Linq-Abfragen wird jedoch von Personen verursacht.

    Die Gründe dafür sind einfach:
    1) Die Leute verstehen nicht, wie sich Linq von SQL unterscheidet. Linq arbeitet mit geordneten Sequenzen und SQL mit ungeordneten Mengen. Daher fügen einige Linq-Vorgänge SQL extrem ineffiziente Sortieroperatoren hinzu.
    2) Die Arbeitsmechanismen von IQuryable-Anbietern und die Art und Weise, wie Abfragen im DBMS ausgeführt werden, sind nicht bekannt. Mehr im vorherigen Beitrag - habrahabr.ru/post/230479

    Aber es gibt Fehler in Anbietern, die zur Erzeugung von Anfragen führen, die alles andere als optimal sind.

    Im Entity Framework gibt es beispielsweise einen Fehler bei der Verwendung von Navigationseigenschaften:
    context.Orders
           .Where(o => o.Id == id)
           .SelectMany(o => o.OrderLines)
           .Select(l => l.Product)
           .ToList();
    

    Eine solche Abfrage generiert das folgende SQL:
    Viel Code
        [Project1].[Id] AS [Id], 
        [Project1].[OrderDate] AS [OrderDate], 
        [Project1].[UserId] AS [UserId], 
        [Project1].[C1] AS [C1], 
        [Project1].[OrderId] AS [OrderId], 
        [Project1].[ProductId] AS [ProductId], 
        [Project1].[Id1] AS [Id1], 
        [Project1].[Title] AS [Title]
        FROM ( SELECT 
            [Extent1].[Id] AS [Id], 
            [Extent1].[OrderDate] AS [OrderDate], 
            [Extent1].[UserId] AS [UserId], 
            [Join1].[OrderId] AS [OrderId], 
            [Join1].[ProductId] AS [ProductId], 
            [Join1].[Id] AS [Id1], 
            [Join1].[Title] AS [Title], 
            CASE WHEN ([Join1].[OrderId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
            FROM  [dbo].[Orders] AS [Extent1]
            LEFT OUTER JOIN  (SELECT [Extent2].[OrderId] AS [OrderId], [Extent2].[ProductId] AS [ProductId], [Extent3].[Id] AS [Id], [Extent3].[Title] AS [Title]
                FROM  [dbo].[OrderLines] AS [Extent2]
                INNER JOIN [dbo].[Products] AS [Extent3] ON [Extent2].[ProductId] = [Extent3].[Id] ) AS [Join1] ON [Extent1].[Id] = [Join1].[OrderId]
            WHERE [Extent1].[Id] = @p__linq__0
        )  AS [Project1]
        ORDER BY [Project1].[Id] ASC, [Project1].[C1] ASC
    


    In dieser Abfrage können das berechnete Feld und die Sortierung nach ihm von SQL Server nicht optimiert werden, und es ist erforderlich, eine echte Sortierung durchzuführen.

    Wenn Sie jedoch die Anforderung von Linq, den Join-Operator zu verwenden, leicht umschreiben, gibt es kein Problem:
    var orders1 = from o in context.Orders
                    where o.Id == id
                    join ol in context.OrderLines on o.Id equals ol.OrderId into j
                    from p in  j.DefaultIfEmpty()
                    select p.Product;
    orders1.ToArray();
    

    Die resultierende SQL:
    SELECT 
        [Extent3].[Id] AS [Id], 
        [Extent3].[Title] AS [Title]
        FROM   [dbo].[Orders] AS [Extent1]
        LEFT OUTER JOIN [dbo].[OrderLines] AS [Extent2] ON [Extent1].[Id] = [Extent2].[OrderId]
        LEFT OUTER JOIN [dbo].[Products] AS [Extent3] ON [Extent2].[ProductId] = [Extent3].[Id]
        WHERE [Extent1].[Id] = @p__linq__0
    

    Es wird von Indizes gut abgedeckt und von SQL Server optimiert.

    Ich habe auch von den ineffizienten Anfragen von NHibernate gehört, aber nicht so aktiv damit gearbeitet, um solche Fehler zu finden.

    Mythos Nummer 3


    Die Zuordnung ist langsam.

    Datareader sich in eine Menge von Objekten Transformation wird in einem Bruchteil ausgeführt Mikro Sekunden für jedes Objekt. Darüber hinaus gelingt dies dem linq2db-Anbieter schneller als dem angekündigten Dapper.

    Was jedoch langsam funktionieren kann, ist das Anhängen der empfangenen Objekte an den Change-Tracking-Kontext. Dies muss jedoch nur erfolgen, wenn die Objekte geändert und in die Datenbank geschrieben werden. In anderen Fällen können Sie explizit angeben, dass die Objekte nicht zum Kontext gehören oder Projektionen verwenden.

    Mythos Nummer 4


    Abfragen werden langsam generiert.

    In der Tat erfordert das Generieren einer SQL-Abfrage aus Linq einen Tree Walk und viel Arbeit mit der Reflektion und Analyse von Metadaten. Bei allen Anbietern wird diese Analyse jedoch einmal ausgeführt, und die Daten werden dann zwischengespeichert.

    Infolgedessen wird bei einfachen Abfragen die Abfrageerzeugung im Durchschnitt für 0,4 ms ausgeführt. Bei komplexen kann es bis zu mehreren Millisekunden dauern.
    Diese Zeit ist normalerweise kleiner als der statistische Fehler der gesamten Ausführungszeit der Abfrage.

    Mythos Nummer 5


    Sie können keine Hinweise verwenden.

    SQL Server verfügt über einen Plan Guide-Mechanismus, mit dem Sie jeder Abfrage Hinweise hinzufügen können. Ähnliche Mechanismen existieren in anderen DBMS.

    Bei der Verwendung von Linq sind jedoch nur wenige Hinweise erforderlich. Linq generiert relativ einfache Abfragen, die vom DBMS unabhängig optimiert werden, wenn Statistiken, Indizes und Einschränkungen vorliegen. Es ist besser, Sperrhinweise zu ersetzen, indem Sie die richtigen Isolationsstufen festlegen und die Anzahl der angeforderten Zeilen begrenzen.

    Mythos Nummer 6


    Linq kann nicht alle Funktionen von SQL nutzen.

    Dies ist teilweise richtig. Viele SQL-Features können jedoch in Funktionen oder Ansichten eingeschlossen werden und werden bereits in Linq-Abfragen verwendet.

    Darüber hinaus können Sie mit dem Entity Framework beliebige SQL-Abfragen ausführen und die Ergebnisse Objekten zuordnen, einschließlich Änderungsnachverfolgung.

    Mythos Nummer 7


    Gespeicherte Prozeduren werden schneller ausgeführt als von Linq generierte Ad-hoc-Anforderungen.

    Dies war Mitte der 90er Jahre der Fall. Heute „kompilieren“ alle DBMS-Systeme Anforderungen und Cache-Pläne, unabhängig davon, ob es sich um eine Ad-hoc- oder eine Ad-hoc-Anforderung handelt.

    Hier ist eine kurze Reihe von Mythen zu stoßen. Wenn Sie mehr haben - ergänzen.

    Jetzt auch beliebt: