Lektionen des Moduls (4/4)
Das N+1-Problem
Einer der häufigsten Fehler in der Backend-Entwicklung, der die API-Leistung katastrophal beeinträchtigt, ist das berüchtigte „N + 1 Abfragen“-Problem.
Es schleicht sich normalerweise ein, ohne dass der Entwickler es weiß, der Bibliotheksabstraktionen wie „ORMs“ (Object-Relational Mappers: z. B. Prisma oder Sequelize) verwendet, um von Node.js aus mit der Datenbank zu kommunizieren.
Anatomie der Katastrophe
Stellen Sie sich vor, eine App möchte die gesamte Auflistung der E-Commerce-Homepage zurückgeben: Alle Produkte zusammen mit der Zeichenfolge des Kategorienamens, zu dem jedes Produkt gehört. Von der Node.js-Seite aus gesehen würde ein unerfahrener Programmierer die iterative Schleife schreiben:
products = await db.query("SELECT * FROM products");(Eine große, umfangreiche Operation 1)- Der Entwickler iteriert (
for..of) über jedenproductim Array von 100 gefundenen - Bei jedem Schritt warten sie in Node.js auf eine iterative Unterabfrage
categoryName = await db.query("SELECT name FROM categories WHERE id = " + product.category_id);und betten sie in das Ergebnis ein (N sekundäre Iterationen langsamer, winziger Abfragen!)
Anfragen an den Postgres-Daemon insgesamt: 1 + 100 lokale Iterationen = 101 Mikronetzwerk-Roundtrips zwischen Node.js und der Datenbank. 101 Roundtrips verursachen einen enormen Netzwerk-Overhead und eine hässliche Latenz. Im realen Maßstab beträgt N möglicherweise nicht 100, sondern 10.000 zu erkundende Rechnungszeilen! Die API würde nicht zurückkehren, bevor ein 5xx-Timeout erreicht wurde.
Die echte native Antwort: JOIN oder Aggregation in einer einzelnen Abfrage
Durch den meisterhaften Einsatz manuell geschriebener DBMS-Abstraktionen anstelle trivialer ORM-Befehle in Node werden die Ausführungen in Millisekunden in einer einzigen, unschlagbaren Abfrage zusammengeführt, die das „N+1“-Chaos löst:
SELECT
p.id,
p.name,
c.name AS category_name
FROM products p
JOIN categories c ON p.category_id = c.id;API/DB-Roundtrips insgesamt: 1 eindeutige Abfrage, fusioniert, mathematisch optimiert durch die eigenen Indizes von Postgres, vollständig mit 0 Millisekunden zusätzlicher Netzwerklatenz verarbeitet.
JSON-Aggregationsalternative
Mit modernen Postgres-Versionen können Sie sogar den gesamten komplexen Join als JSON mit json_agg verschachtelt zurückgeben, sodass Ihr JavaScript das products-Array einschließlich seines Unterarrays formatierter Bewertungen ohne lokale Skriptbindung empfangen kann! (In den fortgeschrittenen Pfaden und Herausforderungen lernen Sie die JSONb-Klausel von Postgres kennen.)
Du bist dran
Lassen Sie uns ein echtes N+1-Problem lösen. Node hätte eine globale Auswahl ausgeführt und dann alle Kunden durchlaufen, um deren Namen zu finden, wenn eine Zusammenfassung der heute Morgen versandten Bestellungen gewünscht worden wäre. Lösen Sie es, indem Sie mir in einem einzigen nativen Schuss einen JOIN zwischen „Kunden“ (verwenden Sie den ursprünglichen Textalias „c“) und „Bestellungen“ (Alias „o“) drucken. Extrahieren Sie zwei triviale Felder (erzwingen Sie die formale Präfixierung eines Alias): „c.first_name“ (vom Kunden) und „o.status“ (von der ausstehenden Bestellung).
Hinweis anzeigen
Syntax: SELECT c.first_name, o.status FROM customer c JOINorders o ON c.id = o.customer_id;
Lösung nach 3 Versuchen verfügbar
Erkunden der Effizienz von GROUP BY
Wenn innerhalb der Datenbank keine logischen Group-Bys verwendet werden, um die Gesamtzahl pro Zeile und Kategorie zu erhalten, würde Node.js die Datenbank endlos aufrufen und sich auf N+1 auflösen. Schreiben Sie eine einzelne Abfrage, die das Feld „Stadt“ aus „Kunden“ und die Standard-Postgres-Zählung „COUNT(id)“ auswählt, indem Sie das Schlüsselwort „terminal“ verwenden, um sie eindeutig nach Namen zu aggregieren.
Hinweis anzeigen
Eine Trivialität: SELECT Stadt, COUNT(id) FROM Kunden GROUP BY Stadt;
Lösung nach 3 Versuchen verfügbar