Hallo! Ich bin Valerio, ein italienischer Entwickler und CTO der Plattform
Inspector.dev.
In diesem Artikel werde ich eine Reihe von ORM-Optimierungsstrategien vorstellen, die ich bei der Entwicklung von Backend-Diensten verwende.
Ich bin sicher, jeder von uns musste sich beschweren, dass der Server oder die Anwendung langsam läuft (oder überhaupt nicht funktioniert) und während der Zeit an der Kaffeemaschine auf die Ergebnisse einer langen Anfrage wartet.
Wie man es repariert?
Lass es uns herausfinden!
Die Datenbank ist eine gemeinsam genutzte Ressource
Warum verursacht die Datenbank so viele Leistungsprobleme?
Wir vergessen oft, dass keine Abfrage unabhängig von anderen ist.
Wir denken, dass selbst wenn eine Abfrage langsam ist, andere kaum betroffen sind ... Aber ist es wirklich so?
Eine Datenbank ist eine gemeinsam genutzte Ressource, die von allen Prozessen verwendet wird, die in Ihrer Anwendung ausgeführt werden. Selbst eine schlecht konzipierte Methode für den Zugriff auf die Datenbank kann die Leistung des gesamten Systems beeinträchtigen.
Vergessen Sie daher nicht die möglichen Konsequenzen und denken Sie: "Es ist in Ordnung, dass dieser Code nicht optimiert ist!" Ein langsamer Zugriff auf die Datenbank kann zu einer Überlastung führen, was sich wiederum negativ auf die Benutzererfahrung auswirken kann.
N + 1 Datenbankabfrageproblem
Was ist das N + 1-Problem?
Dies ist ein häufiges Problem bei der Verwendung eines ORM zur Interaktion mit einer Datenbank. Es geht nicht darum, SQL-Code zu schreiben.
Bei Verwendung eines ORM-Systems wie Eloquent ist nicht immer klar, welche Abfragen wann ausgeführt werden. Lassen Sie uns im Zusammenhang mit diesem speziellen Thema über Beziehungen und eifriges Laden sprechen.
Mit jedem ORM-System können Sie Beziehungen zwischen Entitäten deklarieren und eine hervorragende API für die Navigation in Ihrer Datenbankstruktur bereitstellen.
Nachfolgend finden Sie ein gutes Beispiel für die Entitäten "Artikel" und "Autor".
/*
* Each Article belongs to an Author
*/
$article = Article::find("1");
echo $article->author->name;
/*
* Each Author has many Articles
*/
foreach (Article::all() as $article)
{
echo $article->title;
}
Wenn Sie jedoch Beziehungen innerhalb einer Schleife verwenden, müssen Sie Ihren Code sorgfältig schreiben.
Schauen Sie sich das folgende Beispiel an.
Wir möchten den Namen des Autors neben dem Titel des Artikels einfügen. Mit dem ORM können Sie den Namen des Autors über eine Eins-zu-Eins-Beziehung zwischen dem Artikel und dem Autor abrufen.
Alles scheint einfach zu sein:
// Initial query to grab all articles
$articles = Article::all();
foreach ($articles as $article)
{
// Get the author to print the name.
echo $article->title . ' by ' . $article->author->name;
}
Aber dann sind wir in eine Falle geraten!
Diese Schleife generiert eine erste Anforderung, um alle Artikel abzurufen:
SELECT * FROM articles;
und N weitere Abfragen, um den Autor jedes Artikels zu ermitteln und den Wert des Felds "Name" anzuzeigen, auch wenn der Autor immer derselbe ist.
SELECT * FROM author WHERE id = [articles.author_id]
Wir erhalten genau N + 1 Anfragen.
Dies scheint keine große Sache zu sein. Lassen Sie uns fünfzehn oder zwanzig zusätzliche Anfragen stellen - keine große Sache. Kehren wir jedoch zum ersten Teil dieses Artikels zurück:
- — , .
- , , .
- , .
:
Laut den Laravel-Dokumenten besteht eine gute Chance, dass Sie auf das N + 1-Abfrageproblem stoßen, da beim Zugriff auf eloquente Beziehungen als Eigenschaften (
$article->author) die Beziehungsdaten verzögert geladen werden.
Dies bedeutet, dass die Beziehungsdaten erst geladen werden, wenn Sie zum ersten Mal auf die Eigenschaft zugreifen.
Mit einer einfachen Methode können wir jedoch alle Beziehungsdaten gleichzeitig laden. Wenn Sie dann auf die Eloquent-Beziehung als Eigenschaft zugreifen, führt das ORM-System keine neue Abfrage aus, da die Daten bereits geladen wurden.
Diese Taktik wird als "eifriges Laden" bezeichnet und von allen ORMs unterstützt.
// Eager load authors using "with".
$articles = Article::with('author')->get();
foreach ($articles as $article)
{
// Author will not run a query on each iteration.
echo $article->author->name;
}
Eloquent bietet eine Methode
with()zum eifrigen Laden von Beziehungen.
In diesem Fall werden nur zwei Abfragen ausgeführt.
Der erste wird benötigt, um alle Artikel herunterzuladen:
SELECT * FROM articles;
Die zweite wird von der Methode ausgeführt
with()und ruft alle Autoren ab:
SELECT * FROM authors WHERE id IN (1, 2, 3, 4, ...);
Die interne Engine von Eloquent ordnet die Daten zu und kann auf die übliche Weise aufgerufen werden:
$article->author->name;
Optimieren Sie Ihre Bediener select
Lange Zeit dachte ich, dass das explizite Deklarieren der Anzahl der Felder in einer ausgewählten Abfrage nicht zu einer signifikanten Leistungsverbesserung führte. Der Einfachheit halber habe ich alle Felder in meinen Abfragen erhalten.
Darüber hinaus erschwert die Hardcodierung der Liste der Felder in einer bestimmten select-Anweisung die weitere Pflege eines solchen Codeteils.
Die größte Gefahr dieses Arguments besteht darin, dass dies aus Datenbanksicht tatsächlich zutreffen kann.
Wir arbeiten jedoch mit einem ORM, sodass die aus der Datenbank ausgewählten Daten auf der PHP-Seite in den Speicher geladen werden, damit das ORM-System sie weiter verwalten kann. Je mehr Felder wir erfassen, desto mehr Speicher benötigt der Prozess.
Laravel Eloquent bietet eine Auswahlmethode, um die Abfrage nur auf die Spalten zu beschränken, die wir benötigen:
$articles = Article::query()
->select('id', 'title', 'content') // The fields you need
->latest()
->get();
Durch das Ausschließen von Feldern muss der PHP-Interpreter keine unnötigen Daten verarbeiten, sodass Sie den Speicherverbrauch erheblich reduzieren können.
Das Vermeiden des vollständigen Abrufs kann auch die Leistung beim Sortieren, Gruppieren und Zusammenführen verbessern, da die Datenbank selbst dadurch Speicherplatz sparen kann.
Verwenden Sie Ansichten in MySQL
Ansichten sind SELECT-Abfragen, die auf anderen Tabellen basieren und in der Datenbank gespeichert sind.
Wenn wir eine oder mehrere Tabellen AUSWÄHLEN, kompiliert die Datenbank zuerst unsere SQL-Anweisung, stellt sicher, dass sie fehlerfrei ist, und ruft dann die Daten ab.
Eine Ansicht ist eine vorkompilierte SELECT-Anweisung, die MySQL bei der Verarbeitung sofort die zugrunde liegende interne Abfrage der Ansicht ausführt.
Außerdem ist MySQL beim Filtern von Daten normalerweise intelligenter als PHP. Es gibt erhebliche Leistungssteigerungen bei der Verwendung von Ansichten gegenüber der Verwendung von PHP-Funktionen zum Verarbeiten von Sammlungen oder Arrays.
Wenn Sie mehr über die Funktionen von MySQL zur Entwicklung datenbankintensiver Anwendungen erfahren möchten, besuchen Sie diese großartige Website: www.mysqltutorial.org
Verknüpfen Sie ein eloquentes Modell mit einer Ansicht
Ansichten werden auch als "virtuelle Tabellen" bezeichnet. Aus Sicht des ORM sehen sie wie normale Tabellen aus.
Daher können Sie ein eloquentes Modell erstellen, um die in der Ansicht enthaltenen Daten abzufragen.
class ArticleStats extends Model
{
/**
* The name of the view is the table name.
*/
protected $table = "article_stats_view";
/**
* If the resultset of the View include the "author_id"
* we can use it to retrieve the author as normal relation.
*/
public function author()
{
return $this->belongsTo(Author::class);
}
}
Beziehungen funktionieren wie gewohnt, ebenso wie Zwänge, Paginierung usw. Und es gibt keine Leistungseinbußen.
Fazit
Ich hoffe, diese Tipps helfen Ihnen bei der Entwicklung zuverlässigerer und skalierbarer Software.
Alle Codebeispiele wurden mit Eloquent als ORM geschrieben. Beachten Sie jedoch, dass diese Strategien für alle wichtigen ORMs gleich funktionieren.
Wie ich oft sage, brauchen wir Werkzeuge, um wirksame Strategien umzusetzen. Und wenn es keine Strategie gibt, gibt es nichts zu besprechen.
Vielen Dank, dass Sie den Artikel bis zum Ende gelesen haben. Wenn Sie mehr über Inspector erfahren möchten, lade ich Sie auf unsere Website www.inspector.dev ein . Zögern Sie nicht, dem Chat zu schreiben, wenn Sie Fragen haben!
Zuvor hier gepostet: www.inspector.dev/make-your-application-scalable-optimizing-the-orm-performance
Weiterlesen: