Vai al contenuto
eLearner.app
Modulo 10 · Lezione 4 di 440/57 nel corso~12 min
Lezioni del modulo (4/4)

Il problema N+1

In ambito di sviluppo backend, uno degli errori più frequenti per abbattere catastroficamente le prestazioni di un API è il famigerato problema chiamato "N + 1 Queries".

Esso solitamente insorge ad insaputa dello sviluppatore che sta utilizzando astrazioni di librerie come gli "ORM" (Object-Relational Mapping: es. Prisma o Sequelize) per l'interazione via NodeJS col database.

Logica del Disastro

Consideriamo che un'app voglia restituire un intero listato della homepage eCommerce: Tutti i Prodotti unitamente alla stringa con il nome della Categoria d'appartenenza di ogununo. Visto dallo spettro del NodeJS, uno programmatore inesperto programmerebbe il ciclo iterativo:

  1. products = await db.query("SELECT * FROM products"); (Una grande ed ingombrante operazione 1)
  2. Lo sviluppatore cicla (for..of) per ogni array product dei 100 ritrovati
  3. A comando, in NodeJs aspetta sub-query iterativa categoryName = await db.query("SELECT name FROM categories WHERE id = " + product.category_id); e lo incastra sul risultato (N iterazioni secondarie di queries lente e piccoline!)

Totale richieste al demone Postgres: 1 + 100 iterazioni locali = 101 micro-transazioni di rete fra NodeJs e il DB. 101 viaggi di roundtrip causano overhead di rete paurosi, latenza immonda. E su scala reale potresti avere N che vale non 100, ma 10.000 righe di fatture totali da esplorare! L'API non ritornerebbe i colli di bottiglia prima del timeout 5xx!

La Vera Risposta Nativa: JOIN o Aggregation in singola Query

L'uso ed applicazione magistrale delle astrazioni di un DBMS, scritte a mano invece che con i comandi banali di un ORM generico in Node, fondono le esecuzioni congiunte ed in millisecondi in 1 singola ed ineguagliabile Query per risolvere il patatrac "N+1":

SQL
SELECT
  p.id,
  p.name,
  c.name AS category_name
FROM products p
JOIN categories c ON p.category_id = c.id;

Totale Transazioni Roundtrip API/DB: 1 Query unica, fusa, ottimizzata matematicamente dagli indici da Postgres stesso, gestita interamente a 0 millisecondi di latenza di rete extra.

Alternativa con astrazione JSON

Le versioni moderne di Postgres ti permettono addirittura di far ritornare l'intero innesto della join complessa già sotto forma di JSON aggrovigliato con json_agg , permettendo al tuo JavaScript di ricevere l'array prodotti compreso del loro sub-array di recensioni formattate senza incastri di script locali! (Nei percorsi e sfide avanazate studierai la JSONb clause di postgres).

Prova tu

Esercizio#sql.m10.l4.e1
Tentativi: 0Caricamento…

Risolviamo un vero N+1 Problem nascente.\nNode avrebbe fatto una Select globale e poi iterato tutti clienti per trovarne i nomi se voleva fare un recap di ordini spediti stamani.\nRisolvilo stampandomi in singola mandata nativa una JOIN tra 'customers' (usa l'alias testuale originario 'c') e gli 'orders' (alias 'o').\nEstrai 2 banali campi (obbliga la prefissione formale ad alias): 'c.first_name' (dal cliente) e 'o.status' (proveniente dall'ordinativo in pending).

Caricamento editor…
Mostra suggerimento

Sintassi: SELECT c.first_name, o.status FROM customers c JOIN orders o ON c.id = o.customer_id;

Soluzione disponibile dopo 3 tentativi

Esplorare l'efficienza della GROUP BY

Esercizio#sql.m10.l4.e2
Tentativi: 0Caricamento…

Se non si usano Group-By logici internamente al database, per ottenere il conteggio totale di ogni riga e categoria, NodeJs richiamerebbe il DB infinite volte sfondando N+1.\nScrivi una Query singola che seleziona il campo 'city' da 'customers' ed il calcolo speciale di conta standard Postgres 'COUNT(id)' usando la keyword terminale per aggregrarli unicamente rispetto per nome.

Caricamento editor…
Mostra suggerimento

Un banalissimo: SELECT city, COUNT(id) FROM customers GROUP BY city;

Soluzione disponibile dopo 3 tentativi