Direkt zum Hauptinhalt springen
eLearner.app
Modul 10 · Lektion 4 von 440/57 im Kurs~12 min
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:

  1. products = await db.query("SELECT * FROM products"); (Eine große, umfangreiche Operation 1)
  2. Der Entwickler iteriert (for..of) über jeden product im Array von 100 gefundenen
  3. 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:

SQL
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

Übung#sql.m10.l4.e1
Versuche: 0Wird geladen…

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).

Editor wird geladen…
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

Übung#sql.m10.l4.e2
Versuche: 0Wird geladen…

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.

Editor wird geladen…
Hinweis anzeigen

Eine Trivialität: SELECT Stadt, COUNT(id) FROM Kunden GROUP BY Stadt;

Lösung nach 3 Versuchen verfügbar