Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby on Rails

Verbesserung von der Leistung Ihrer Rails-App durch eifriges Laden

by
Length:LongLanguages:

German (Deutsch) translation by Tatsiana Bochkareva (you can also view the original English article)

Die Benutzer mögen blitzschnelle Anwendungen, verlieben sich dann in sie und machen sie zu einem Teil ihres Lebens. Langsame Anwendungen hingegen ärgern nur Benutzer und verlieren Einnahmen. In diesem Tutorial stellen wir sicher, dass wir nicht mehr Geld oder Benutzer verlieren, und verstehen die verschiedenen Möglichkeiten zur Leistungsverbesserung.

Active Records und ORM sind in Ruby on Rails sehr leistungsfähige Tools, aber nur, wenn wir wissen, wie wir diese Leistung entfalten und nutzen können. Am Anfang finden Sie unzählige Möglichkeiten, eine ähnliche Aufgabe in RoR auszuführen, aber nur wenn Sie etwas tiefer graben, lernen Sie tatsächlich die Kosten für die Verwendung übereinander kennen.

Ähnlich verhält es sich mit ORM und Associations in Rails. Sie machen unser Leben sicher viel einfacher, können aber in manchen Situationen auch als Overkill wirken.

Betrachten wir ein Beispiel

Aber vorher generieren wir schnell eine Dummy-Anwendung, mit der wir herumspielen können.

Schritt 1

Starten Sie Ihr Terminal und geben Sie die folgenden Befehle ein, um eine neue Anwendung zu erstellen:

Schritt 2

Generieren Sie Ihre Anwendung:

Schritt 3

Stellen Sie es auf Ihrem lokalen Server bereit:

Und das war es! Jetzt sollten Sie eine laufende Dummy-Anwendung haben.

So sollten unsere beiden Modelle (Autor und Beitrag) aussehen. Wir haben Beiträge, die dem Autor gehören, und wir haben Autoren, die viele Beiträge haben können. Dies ist die sehr grundlegende Assoziation / Beziehung zwischen diesen beiden Modellen, mit denen wir spielen werden.

Schauen Sie sich Ihren "Posts Controller" an - so sollte er aussehen. Unser Hauptaugenmerk wird nur auf der Indexmethode liegen.

Und zu guter Letzt unsere Posts Index View. Ihre scheinen einige zusätzliche Zeilen zu haben, aber ich möchte, dass Sie sich auf diese konzentrieren, insbesondere auf die Zeile mit post.author.name.

Lassen Sie uns einfach einige Dummy-Daten erstellen, bevor wir beginnen. Gehen Sie zu Ihrer Rails-Konsole und fügen Sie die folgenden Zeilen hinzu. Oder Sie können einfach zu http://localhost:3000/posts/new und http://localhost:3000/authors/new gehen, um einige Daten manuell hinzuzufügen.

Nachdem Sie alles eingerichtet sind, starten wir den Server mit rails s und drücken auf localhost:3000/posts.

Auf diesem Bildschirm werden einige Ergebnisse angezeigt.

This image shows the page displaying all the Posts

Es scheint also alles in Ordnung zu sein: keine Fehler, und es werden alle Datensätze zusammen mit den zugehörigen Autorennamen abgerufen. Wenn Sie sich jedoch Ihr Entwicklungsprotokoll ansehen, werden Sie feststellen, dass unzählige Abfragen wie unten ausgeführt werden.

Okay, ich stimme zu, dass dies nur vier Abfragen sind, aber stellen Sie sich vor, Sie haben 3.000 Beiträge in Ihrer Datenbank anstatt nur drei. In diesem Fall wird unsere Datenbank mit 3.000 + 1-Abfragen überflutet, weshalb dieses Problem als N+1-Problem bezeichnet wird.

Warum bekommen wir dieses Problem?

In Ruby on Rails ist für das ORM standardmäßig das verzögerte Laden aktiviert, was bedeutet, dass das Laden von Daten bis zu dem Punkt verzögert wird, an dem wir sie tatsächlich benötigen.

In unserem Fall ist es zuerst der Controller, bei dem er aufgefordert wird, alle Beiträge abzurufen.

Zweitens ist die Ansicht, in der wir die vom Controller abgerufenen Beiträge durchlaufen und eine Abfrage senden, um den Autorennamen für jeden Beitrag separat zu erhalten. Daher das N+1-Problem.

Wie lösen wir das Problem?

Um uns aus solchen Situationen zu retten, bietet uns Rails eine Funktion namens Eager Loading.

Durch eifriges Laden können Sie die zugehörigen Daten (Authors) für alle Posts aus der Datenbank vorab laden, die Gesamtleistung verbessern, indem Sie die Anzahl der Abfragen reduzieren, und Sie erhalten die Daten, die Sie in Ihren Ansichten anzeigen möchten, aber der einzige Haken hier ist welches zu verwenden. Erwischt!

Ja, weil wir drei davon haben und alle dem gleichen Zweck dienen, aber je nach Fall kann sich herausstellen, dass jeder von ihnen die Leistung wieder verringert oder übertrifft.

Nun könnten Sie fragen, welche in diesem Fall zu verwenden ist? Beginnen wir mit dem ersten.

Speichern Sie es. Klicken Sie erneut auf die URL localhost:3000/posts.

This image shows the page displaying all the Posts

Also keine Änderungen an den Ergebnissen: Alles wird genauso geladen, aber unter der Haube im Entwicklungsprotokoll wurden diese Tonnen von Abfragen auf die folgenden zwei geändert.

Preload verwendet zwei separate Abfragen, um die Hauptdaten und die zugehörigen Daten zu laden. Dies ist tatsächlich viel besser als eine separate Abfrage für jeden Autorennamen (das N+1-Problem), aber dies reicht uns nicht aus. Aufgrund seines separaten Abfrageansatzes wird in folgenden Szenarien eine Ausnahme ausgelöst:

  1. Sortieren Sie Publikationen nach Autorennamen.
  2. Finden Sie nur Publikationen des Autors "John".

Probieren wir alle Szenarien mit eager_load() nacheinander aus

1. Ordnen Sie die Beiträge nach dem Namen des Autors

Resultierende Abfrage in den Entwicklungsprotokollen:

2. Finden Sie nur Beiträge des Autors "John"

Resultierende Abfrage in den Entwicklungsprotokollen:

3. N+1-Szenario

Resultierende Abfrage in den Entwicklungsprotokollen:

Wenn Sie sich also die resultierenden Abfragen aller drei Szenarien ansehen, gibt es zwei Gemeinsamkeiten.

Erstens verwendet eager_load() immer den LEFT OUTER JOIN, egal in welchem Fall. Zweitens werden alle zugehörigen Daten in einer einzigen Abfrage abgerufen, wodurch die preload() -Methode in Situationen, in denen wir die zugehörigen Daten für zusätzliche Aufgaben wie Bestellen und Filtern verwenden möchten, sicher übertroffen wird. Eine einzelne Abfrage und LEFT OUTER JOIN können jedoch auch in einfachen Szenarien wie oben sehr teuer sein, in denen Sie lediglich die benötigten Autoren filtern müssen. Es ist, als würde man eine Panzerfaust mit einer Panzerfaust töten.

Ich verstehe, dass dies nur zwei einfache Beispiele sind, und in realen Szenarien kann es sehr schwierig sein, sich für das zu entscheiden, das für Ihre Situation am besten geeignet ist. Das ist der Grund, warum Rails uns die include() -Methode gegeben hat.

Mit include() kümmert sich Active Record um die schwierige Entscheidung. Es ist viel intelligenter als die Methoden preload() und eager_load() und entscheidet selbst, welche Methode verwendet werden soll.

Probieren wir alle Szenarien mit Includes() aus

1. Ordnen Sie die Beiträge nach dem Namen des Autors

Resultierende Abfrage in den Entwicklungsprotokollen:

2. Finden Sie nur Beiträge des Autors "John"

Resultierende Abfrage in den Entwicklungsprotokollen:

3. N+1-Szenario

Resultierende Abfrage in den Entwicklungsprotokollen:

Wenn wir nun die Ergebnisse mit der eager_load() -Methode vergleichen, haben die ersten beiden Fälle ähnliche Ergebnisse, aber im letzten Fall wurde klugerweise beschlossen, zur besseren Leistung auf die preload() -Methode umzusteigen.

Genial, richtig?

Nein, denn in diesem Leistungsrennen kann manchmal auch das eifrige Laden zu kurz kommen. Ich hoffe, einige von Ihnen haben bereits bemerkt, dass eifrige Lademethoden, wenn sie JOINS verwenden, nur LEFT OUTER JOIN verwenden. Außerdem laden sie in jedem Fall zu viele unnötige Daten in den Speicher - sie wählen jede einzelne Spalte aus der Tabelle aus, während wir nur den Namen des Autors benötigen.

Willkommen bei den Joins

Obwohl Sie mit Active Record genau wie joins() Bedingungen für die eifrig geladenen Zuordnungen festlegen können, wird empfohlen, stattdessen Joins zu verwenden. ~ Schienen-Dokumentation.

Wie in der Rails-Dokumentation empfohlen, ist die Methode joins() in diesen Situationen einen Schritt voraus. Es verbindet sich mit der zugehörigen Tabelle, lädt jedoch nur die erforderlichen Modelldaten wie in unserem Fall wie posts in den Speicher. Daher laden wir redundante Daten nicht unnötig in den Speicher - obwohl wir dies auch tun können, wenn wir möchten.

Lassen Sie uns in einige Beispiele eintauchen

1. Ordnen Sie die Beiträge nach dem Namen des Autors

Resultierende Abfrage in den Entwicklungsprotokollen:

2. Finden Sie nur Beiträge des Autors "John"

Resultierende Abfrage in den Entwicklungsprotokollen:

3. N+1-Szenario

Resultierende Abfrage in den Entwicklungsprotokollen:

Das erste, was Sie aus den obigen Ergebnissen ersehen können, ist, dass das N+1-Problem zurück ist, aber konzentrieren wir uns zuerst auf den guten Teil.

Schauen wir uns die erste Abfrage aller Ergebnisse an. Alle sehen mehr oder weniger so aus.

Es ruft alle Spalten von Posts ab. Es verbindet sowohl die Tabellen als auch sortiert oder filtert die Datensätze je nach Bedingung, ohne jedoch Daten aus der zugehörigen Tabelle abzurufen. Welches ist, was wir in erster Linie wollten.

Nach den ersten Abfragen werden jedoch je nach den Daten in Ihrer Datenbank 1, 3 oder N Abfragen angezeigt:

Nun könnten Sie sich fragen: Warum ist dieses N+1-Problem zurück? Es liegt an dieser Zeile in unserer Ansicht post.author.name.

Diese Zeile löst alle diese Abfragen aus. In dem Beispiel, in dem wir nur unsere Beiträge bestellen mussten, müssen wir den Namen des Autors in unseren Ansichten nicht anzeigen. In diesem Fall können wir dieses Problem beheben, indem wir die Zeile post.author.name aus der Ansicht entfernen.

Aber dann könnten Sie fragen: "Hey MK, was ist mit den Beispielen, in denen wir den Namen des Autors in der Ansicht anzeigen möchten?"

In diesem Fall wird die Methode joins() das Problem nicht von selbst beheben. Wir müssen joins() anweisen, den Namen des Autors oder eine andere Spalte aus der Tabelle auszuwählen. Und wir können dies tun, indem wir am Ende eine select() - Anweisung hinzufügen, wie folgt:

Ich habe einen Alias "Autorenname" für Autoren.Name erstellt. Wir werden in nur einer Sekunde sehen, warum.

Resultierende Abfrage in den Entwicklungsprotokollen:

Los geht's: Endlich eine saubere SQL-Abfrage ohne N+1-Problem, ohne unnötige Daten, mit genau den Dingen, die wir brauchen. Sie müssen nur noch diesen Alias in Ihrer Ansicht verwenden und post.author.name in post.author_name ändern. Das liegt daran, dass der Autorenname jetzt ein Attribut unseres Post-Modells ist. Nach dieser Änderung sieht die Seite folgendermaßen aus:

This image shows the page displaying all the Posts

Alles genau das gleiche, aber unter der Haube wurden viele Dinge geändert. Wenn ich alles auf den Punkt bringe, um das N+1 zu lösen, sollten Sie eager loading, aber manchmal sollten Sie, abhängig von der Situation, die Dinge unter Ihre Kontrolle bringen und joins verwenden, um bessere Optionen zu erhalten. Sie können der joins() -Methode auch weitere SQL-Abfragen zur weiteren Anpassung bereitstellen.

Joins und eifriges Laden ermöglichen auch das Laden mehrerer Assoziationen, aber am Anfang kann es sehr kompliziert und schwierig werden, die beste Option zu finden. In solchen Situationen empfehle ich Ihnen, diese beiden sehr schönen Envato Tuts+ -Tutorials zu lesen, um ein besseres Verständnis der Verknüpfungen zu erhalten und den kostengünstigsten Ansatz in Bezug auf die Leistung zu bestimmen:

Zu guter Letzt kann es schwierig sein, Bereiche in Ihrer vorgefertigten Anwendung herauszufinden, in denen Sie die Leistung allgemein verbessern oder die N+1-Probleme finden sollten. In diesen Fällen empfehle ich ein schönes Juwel namens Bullet. Es kann Sie benachrichtigen, wenn Sie eifriges Laden für N+1-Abfragen hinzufügen sollten und wenn Sie eifriges Laden unnötig verwenden.

Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.