PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

Optymalizacja wydajności – OPcache, Redis, N+1 queries, Blackfire, tabela priorytetów

by Henryk Tews / wtorek, 14 lutego 2023 / Opublikowano w Magento 2

Wolny sklep to utracone przychody. Każde 100ms opóźnienia to mierzalny spadek konwersji. Optymalizacja Magento 2 to nie magia – to systematyczna diagnostyka i eliminacja wąskich gardeł. Pokazuję jak zidentyfikować co spowalnia sklep, jak mierzyć poprawę i które zmiany dają największy efekt w stosunku do nakładu pracy.

Narzędzia diagnostyczne – zacznij od pomiaru

Nie optymalizuj na ślepo. Najpierw zmierz gdzie jest problem:

# Blackfire - profesjonalny profiler PHP (polecany dla Magento)
# Instalacja agenta i probe, potem profilowanie przez CLI lub przeglądarkę
blackfire run php bin/magento catalog:reindex

# Tideways XHProf - darmowa alternatywa
composer require tideways/xhprof

# New Relic APM - monitoring produkcyjny
# Pokazuje slow queries, zewnętrzne wywołania, memory usage per request

# Wbudowane narzędzia Magento
bin/magento dev:query-log:enable       # logowanie slow queries
bin/magento dev:template-hints:enable  # pokazuje czas renderowania bloków
<?php

// Prosta diagnostyka czasu ładowania w kodzie
namespace Vendor\Module\Plugin;

class PerformanceLogger
{
    private array $timers = [];

    public function start(string $label): void
    {
        $this->timers[$label] = microtime(true);
    }

    public function stop(string $label): float
    {
        $elapsed = (microtime(true) - ($this->timers[$label] ?? microtime(true))) * 1000;
        unset($this->timers[$label]);

        if ($elapsed > 100) { // loguj tylko jeśli > 100ms
            \Magento\Framework\App\ObjectManager::getInstance()
                ->get(\Psr\Log\LoggerInterface::class)
                ->warning("Slow operation: {$label} took {$elapsed}ms");
        }

        return $elapsed;
    }
}

Indeksery – pierwsza linia optymalizacji

# Sprawdź stan indekserów
bin/magento indexer:status

# Stare indeksy = wolne zapytania
bin/magento indexer:reindex

# Ustaw tryb Update on Schedule zamiast Update on Save
# dla dużych katalogów - unikasz reindeksacji przy każdym zapisie produktu
bin/magento indexer:set-mode schedule catalog_product_price
bin/magento indexer:set-mode schedule cataloginventory_stock
bin/magento indexer:set-mode schedule catalog_category_product

# Flat catalog - dla sklepów z > 50k produktów
bin/magento config:set catalog/frontend/flat_catalog_category 1
bin/magento config:set catalog/frontend/flat_catalog_product 1
bin/magento indexer:reindex catalog_category_flat catalog_product_flat

Cache – konfiguracja dla maksymalnej wydajności

# Sprawdź co zajmuje miejsce w cache
bin/magento cache:status

# Redis dla cache i sesji (omówione we wpisie z 2022-01)
# Kluczowe ustawienia które często są pominięte:

# OPcache - największy wpływ na PHP
# php.ini:
# opcache.enable=1
# opcache.memory_consumption=512     <- nie 128MB jak domyślnie
# opcache.max_accelerated_files=130000  <- Magento ma dużo plików
# opcache.validate_timestamps=0      <- na produkcji wyłącz walidację
# opcache.revalidate_freq=0
# opcache.interned_strings_buffer=16
<?php

// app/etc/env.php - optymalna konfiguracja cache
return [
    'cache' => [
        'frontend' => [
            'default' => [
                'id_prefix' => 'prod_',
                'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis',
                'backend_options' => [
                    'server'             => 'redis-cache',
                    'port'               => 6379,
                    'database'           => 0,
                    'compress_data'      => '1',
                    'compression_lib'    => 'l4z',  // lz4 szybsze niż gzip
                    'compress_threshold' => 2048,
                    'connect_retries'    => 1,
                ],
            ],
            'page_cache' => [
                'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis',
                'backend_options' => [
                    'server'        => 'redis-fpc',
                    'port'          => 6379,
                    'database'      => 0,
                    'compress_data' => '0',  // FPC nie kompresuj
                ],
            ],
        ],
    ],
];

Baza danych – najczęstsze wąskie gardła

# Włącz slow query log w MySQL
# my.cnf:
# slow_query_log = 1
# slow_query_log_file = /var/log/mysql/slow.log
# long_query_time = 1

# Analiza slow query log
mysqldumpslow -s t -t 20 /var/log/mysql/slow.log

# EXPLAIN dla podejrzanych zapytań
# EXPLAIN SELECT * FROM catalog_product_entity WHERE sku IN (...)
<?php

// Częste problemy z N+1 query w Magento
// Problem - N+1 query w pętli
$collection = $this->productCollectionFactory->create();
$collection->addAttributeToSelect('*');

foreach ($collection as $product) {
    // KAŻDA iteracja generuje osobne zapytanie do bazy!
    $category = $this->categoryRepository->get($product->getCategoryId());
    echo $product->getName() . ' - ' . $category->getName();
}

// Rozwiązanie - załaduj dane z joinami w jednym zapytaniu
$collection = $this->productCollectionFactory->create();
$collection->addAttributeToSelect(['name', 'price', 'sku']);
$collection->joinField(
    'category_name',
    'catalog_category_entity_varchar',
    'value',
    'entity_id=entity_id',
    ['store_id' => 0]
);
$collection->setPageSize(100); // zawsze paginuj!

Statyczne zasoby – deployment i CDN

# Deployment statycznych zasobów - produkcja
bin/magento setup:static-content:deploy pl_PL en_US \
    --jobs=$(nproc) \    # równoległe procesy
    -f \                 # force - nawet bez nowych wersji
    --no-html-minify     # pomiń minifikację HTML jeśli masz CDN z kompresją

# Kompresja JS/CSS
bin/magento config:set dev/js/merge_files 1
bin/magento config:set dev/js/enable_js_bundling 1
bin/magento config:set dev/css/merge_css_files 1

# Weryfikacja rozmiaru bundli
ls -lh pub/static/frontend/Vendor/theme/pl_PL/

# CDN - ustaw base URL dla statycznych zasobów
bin/magento config:set web/secure/base_static_url https://cdn.example.com/
bin/magento config:set web/unsecure/base_static_url https://cdn.example.com/

PHP-FPM – tuning procesu

# /etc/php/8.2/fpm/pool.d/magento.conf
[magento]
user  = www-data
group = www-data

# pm = dynamic dla zmiennego ruchu
pm                   = dynamic
pm.max_children      = 50      # RAM / ~50MB per proces
pm.start_servers     = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests      = 500     # recykluj procesy - zapobiega memory leak

# Timeouty
request_terminate_timeout = 300
request_slowlog_timeout   = 10
slowlog = /var/log/php/magento-slow.log

# Zmienne środowiskowe
env[MAGE_MODE] = production
env[MAGE_RUN_CODE] = base

Pomiar efektów – przed i po

# Apache Bench - prosty benchmark HTTP
ab -n 100 -c 10 https://example.com/catalog/category/view/id/42

# Siege - bardziej realistyczny test
siege -c 20 -t 60S https://example.com/

# curl z czasami
curl -o /dev/null -s -w \
    "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
    https://example.com/

# Lighthouse CI - metryki Core Web Vitals
npx lighthouse https://example.com --output=json --quiet \
    | jq '.categories.performance.score'

Priorytetyzacja – co daje największy efekt

Optymalizacja Efekt Nakład pracy
OPcache (prawidłowa konfiguracja) Bardzo wysoki Niski
Redis dla cache i sesji Bardzo wysoki Średni
Varnish FPC Wysoki (strony niezalogowanych) Średni
Reindeksacja + Update on Schedule Wysoki (przy dużym katalogu) Niski
Eliminacja N+1 queries Wysoki (zależy od kodu) Wysoki
CDN dla statycznych zasobów Średni (zależy od ruchu) Średni
PHP-FPM tuning Średni Niski
Flat catalog Średni (> 50k produktów) Niski

Podsumowanie

Optymalizacja Magento 2 to proces, nie jednorazowe działanie. Zacznij od pomiaru – Blackfire lub New Relic pokażą gdzie naprawdę jest problem. OPcache i Redis to fundament który musi być poprawnie skonfigurowany zanim zaczniesz szukać bardziej egzotycznych optymalizacji. N+1 queries w customowym kodzie to często największe rezerwy – i jedyne miejsce gdzie nie wystarczy zmiana konfiguracji, trzeba zrozumieć kod.

About Henryk Tews

Co możesz przeczytać następne

PageBuilder – własny typ zawartości, XML config
Redis – trzy zastosowania, konfiguracja env.php, osobne instancje, monitoring
DI w Magento 2 – kontener, Virtual Types, Factory, shared/non-shared
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Zawsze aktywne
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}