Przejdź do głównej treści
eLearner.app
Moduł 10 · Lekcja 4 z 440/57 w kursie~12 min
Lekcje modułu (4/4)

Problem N+1

Podczas tworzenia backendu jednym z najczęstszych błędów, który katastrofalnie obniża wydajność API, jest niesławny problem „N + 1 zapytań”.

Zwykle wkrada się w sposób nieznany programiście, który używa abstrakcji bibliotecznych, takich jak „ORM” (mapery obiektowo-relacyjne: np. Prisma lub Sequelize), aby komunikować się z bazą danych z Node.js.

Anatomia katastrofy

Wyobraź sobie, że aplikacja chce zwrócić całą listę stron głównych e-commerce: Wszystkie produkty wraz z ciągiem nazwy kategorii, do której każdy należy. Patrząc od strony Node.js, niedoświadczony programista napisałby pętlę iteracyjną:

  1. products = await db.query("SELECT * FROM products"); (Jedna duża, nieporęczna operacja 1)
  2. Programista wykonuje iterację (for..of) po każdym product w tablicy 100 znalezionych
  3. Na każdym kroku w Node.js czekają na iteracyjne podzapytanie categoryName = await db.query("SELECT name FROM categories WHERE id = " + product.category_id); i osadzają je w wyniku (N wtórnych iteracji powolnych, drobnych zapytań!)

Łączna liczba żądań do demona Postgres: 1 + 100 iteracji lokalnych = 101 podróży w obie strony w mikrosieci między Node.js a bazą danych. 101 podróży w obie strony powoduje straszliwe obciążenie sieci i brzydkie opóźnienia. W rzeczywistej skali N może nie wynosić 100, ale 10 000 wierszy wszystkich faktur do zbadania! Interfejs API nie powróciłby przed osiągnięciem limitu czasu 5xx.

Prawdziwa odpowiedź natywna: DOŁĄCZ lub agregacja w jednym zapytaniu

Mistrzowskie wykorzystanie abstrakcji DBMS napisanych ręcznie, zamiast trywialnych poleceń ORM w Node, łączy wykonania w ciągu milisekund w jednym, bezkonkurencyjnym zapytaniu, które rozwiązuje bałagan „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;

Całkowita liczba cykli API/DB: 1 unikalne zapytanie, połączone, zoptymalizowane matematycznie przez własne indeksy Postgres, obsługiwane w całości z dodatkowym opóźnieniem sieci wynoszącym 0 milisekund.

Alternatywa agregacji JSON

Nowoczesne wersje Postgres pozwalają nawet zwrócić całe złożone sprzężenie zagnieżdżone jako JSON z json_agg, dzięki czemu Twój JavaScript może otrzymać tablicę products, w tym podtablicę sformatowanych recenzji, bez żadnego lokalnego kleju skryptowego! (W zaawansowanych ścieżkach i wyzwaniach będziesz studiować klauzulę JSONb Postgresa.)

Twoja kolej

Ćwiczenie#sql.m10.l4.e1
Próby: 0Ładowanie...

Rozwiążmy prawdziwy problem N+1 w trakcie tworzenia. Węzeł uruchomiłby globalny wybór, a następnie iterował po każdym kliencie, aby znaleźć jego nazwiska, gdyby chciał podsumowania zamówień wysłanych dziś rano. Rozwiąż go, drukując mi w jednym natywnym ujęciu JOIN pomiędzy „klientami” (użyj oryginalnego aliasu tekstowego „c”) i „zamówieniami” (aliasem „o”). Wyodrębnij 2 trywialne pola (wymuszając formalny alias) przedrostek): 'c.first_name' (od klienta) i 'o.status' (od oczekującego zamówienia).

Ładowanie edytora...
Pokaż wskazówkę

Składnia: SELECT c.imię_pierwszego, o.status FROM klientów c DOŁĄCZ zamówienia o ON c.id = o.id_klienta;

Rozwiązanie dostępne po 3 próbach

Odkrywanie wydajności GROUP BY

Ćwiczenie#sql.m10.l4.e2
Próby: 0Ładowanie...

Jeśli w bazie danych nie zostaną użyte logiczne metody grupowania w celu uzyskania całkowitej liczby wierszy i kategorii, Node.js będzie wywoływał bazę danych w nieskończoność, zwiększając liczbę N+1. Napisz pojedyncze zapytanie, które wybierze pole „miasto” z pola „klienci” i standardową liczbę Postgres „COUNT(id)”, używając słowa kluczowego terminal, aby agregować je jednoznacznie według nazwy.

Ładowanie edytora...
Pokaż wskazówkę

Banalne: WYBIERZ miasto, COUNT(id) OD klientów GROUP BY miasto;

Rozwiązanie dostępne po 3 próbach