Jeder Backend-Entwickler hat dieses Rätsel schon mindestens einmal erlebt: Du baust einen API-Endpoint, der die letzten Bestellungen samt Namen des jeweiligen Kunden auflistet. Auf deinem lokalen Entwicklungsrechner mit 4 Seed-Datensätzen antwortet der Endpoint in rasanten 6 Millisekunden.
Du deployst in die Produktion. Sobald Traffic reinkommt und die Seite pro Anfrage 100 Bestellungen rendert, schnellt die CPU-Auslastung deines PostgreSQL-Servers auf 100 %, die Connection Pools laufen leer und die Antwortzeiten kriechen auf 3,5 Sekunden.
Der Schuldige? Das berüchtigtste Performance-Antipattern der ORM-Geschichte: das N+1-Query-Problem.
1. Anatomie einer N+1-Query
Sieh dir diesen scheinbar harmlosen Code an, der die letzten 100 Bestellungen lädt und jeweils die Kundendaten anhängt:
// ❌ THE N+1 TRAP: 1 query for orders + 100 separate queries inside the loop!
const orders = await db.query('SELECT * FROM orders LIMIT 100'); // -> 1st Query (1)
for (const order of orders) {
// Executes a brand-new network roundtrip to PostgreSQL on EVERY iteration!
order.user = await db.query('SELECT * FROM users WHERE id = $1', [order.user_id]); // -> 100 Queries (N)
}Rechnen wir nach:
- 1 Query lädt die Liste der
NBestellungen (N = 100). - 100 einzelne Queries laden den Nutzer zu jeder einzelnen Bestellung.
- Gesamt:
100 + 1 = 101Datenbankabfragen für eine einzige HTTP-Anfrage!
Warum dich die Latenz der Netzwerk-Roundtrips umbringt
Selbst wenn PostgreSQL jedes einzelne SELECT * FROM users WHERE id = $1 dank Primärschlüssel-Index in 0.2ms ausführt, braucht jede einzelne Query einen TCP-Roundtrip zwischen deinem API-Container und deinem Datenbankserver (~3ms bis 5ms in Cloud-VPCs).
2 queries × 4ms network overhead = 8ms101 queries × 4ms network overhead = 404ms(für nur einen Nutzer, der die Seite neu lädt!)
2. Die Lösung: Eager Loading (JOIN oder WHERE IN)
Statt der Datenbank 100 einzelne Fragen nacheinander zu stellen, holt Eager Loading alle benötigten Daten mit insgesamt 1 oder 2 Queries.
Variante A: SQL LEFT JOIN (insgesamt 1 Query)
SELECT orders.*, users.name, users.email
FROM orders
LEFT JOIN users ON orders.user_id = users.id
LIMIT 100;Variante B: Batch WHERE id IN (...) (insgesamt 2 Queries)
Moderne ORMs wie Prisma, Drizzle, Eloquent (Laravel) und Django aktivieren Eager Loading mit einer einzigen Eigenschaft:
// ✅ PRISMA: Uses `include` to batch-fetch all related users in 1 extra query (WHERE id IN (...))
const orders = await prisma.order.findMany({
take: 100,
include: {
user: true, // <- Eliminates the N+1 loop!
},
});
// ✅ DRIZZLE ORM: Relational query with `with`
const orders = await db.query.orders.findMany({
limit: 100,
with: {
user: true,
},
});Unter der Haube führt das ORM Folgendes aus:
SELECT * FROM orders LIMIT 100;SELECT * FROM users WHERE id IN (12, 15, 19, 44, ...);
Und setzt die Objekte in Mikrosekunden im Speicher zusammen! Aus 101 Queries werden 2 Queries.
Interaktives Diagnose-Tool
Du willst deine eigenen ORM-Muster testen und unsere Schritt-für-Schritt-Checkliste zur N+1-Erkennung durchgehen?
Interaktive Roadmap & Tool
Junior Backend Developer 2026
Was du wirklich wissen musst, um 2026 eine Junior-Backend-Stelle zu bekommen, und was warten kann – ohne die Buzzword-Fallen aus echten Stellenanzeigen.
SQL oder NoSQL?
Beantworte sechs Fragen, und ich sage dir, welche Art von Datenbank zu deinem Projekt passt und warum.
Als Nächstes lesen
- CZDEDurch den Dschungel: Welche Backend-Sprache und welches Framework solltest du 2026 wählen?
Durch den Dschungel: Welche Backend-Sprache und welches Framework solltest du 2026 wählen?
25. Oktober 2026 · 3 Min. Lesezeit - CZDEPHP CVE-2026-115560 im Detail: Session Upload Progress und Serialize-Handler-Mismatch erklärt
PHP CVE-2026-115560 im Detail: Session Upload Progress und Serialize-Handler-Mismatch erklärt
24. Oktober 2026 · 3 Min. Lesezeit