Strona kategorii ładowała się 18 sekund przy 200 produktach. PageSpeed score: 12/100. Problem nie był w serwerze ani Varnishu – był w kodzie modułu który dla każdego produktu wykonywał osobne zapytanie SQL.
Objawy
- TTFB (Time To First Byte) powyżej 10 sekund
- Czas ładowania strony kategorii rośnie liniowo z liczbą produktów
- MySQL slow query log pełen zapytań typu
SELECT * FROM catalog_product_entity WHERE entity_id = X - Problem tylko na stronach z listą produktów, nie na stronach produktu
Diagnoza
# Włącz MySQL slow query log # /etc/mysql/conf.d/slow.cnf: # slow_query_log = 1 # slow_query_log_file = /var/log/mysql/slow.log # long_query_time = 0.1 # Lub użyj Xdebug profilera # xdebug.mode = profile # Otwórz stronę kategorii z XDEBUG_PROFILE=1 # Przeanalizuj cachegrind - znajdź funkcję wywoływaną 200+ razy
Przyczyna
Moduł „powiązanych produktów” ładował dla każdego produktu z listy osobną kolekcję powiązań. Przy 200 produktach = 200 dodatkowych zapytań SQL. Klasyczny problem N+1.
Rozwiązanie
<?php
// Przed - N+1: jedno zapytanie per produkt
foreach ($products as $product) {
$related = $this->relatedFactory->create()
->addFieldToFilter('parent_id', $product->getId())
->load(); // ZAPYTANIE SQL dla każdego produktu!
}
// Po - jedno zapytanie dla wszystkich
$productIds = array_map(fn($p) => $p->getId(), $products);
$allRelated = $this->relatedFactory->create()
->addFieldToFilter('parent_id', ['in' => $productIds])
->setPageSize(1000)
->load();
// Zgrupuj po parent_id
$grouped = [];
foreach ($allRelated as $rel) {
$grouped[$rel->getParentId()][] = $rel;
}
Wynik
Czas ładowania strony kategorii: z 18s do 1.2s. Liczba zapytań SQL: z 215 do 8. PageSpeed: z 12 do 78.
