sysadmin.ecommerce > log-346-log-346-indeksy-w-mysql-jak-przyspieszyc-zapytania.md
root@prod-01:~/blog# cat log-346-log-346-indeksy-w-mysql-jak-przyspieszyc-zapytania.md
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Data: 2026-06-23 | Kategoria: -- | Czas czytania: 9 min
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Indeksy w MySQL – jak przyspieszyć zapytania w sklepie internetowym. Kompletny poradnik 2026

###02

Wolne zapytania SQL to najczęstsza przyczyna spadku wydajności sklepów internetowych. Gdy katalog produktów przekracza kilka tysięcy rekordów, a ruch rośnie, brak odpowiednich indeksów w MySQL może wydłużyć ładowanie strony z ułamka sekundy do kilkunastu sekund. W tym poradniku pokażę Ci, jak działają indeksy, jak je prawidłowo dobierać i jak uniknąć najczęstszych błędów, które zamiast pomóc — szkodzą.

Konfigurujesz Redis dla swojego sklepu? Sprawdź nasz poradnik: Redis w e-commerce – Rozdzielenie Cache od Sesji (Multi-Instance).

Dlaczego indeksy w MySQL są kluczowe dla e-commerce?

Wyobraź sobie księgarnię z setkami tysięcy książek. Bez katalogu (indeksu) musiałbyś przejrzeć każdą półkę, by znaleźć jeden tytuł. Tak samo działa MySQL — bez indeksu wykonuje full table scan, czyli przegląda wiersz po wierszu całą tabelę. Przy 100 tysiącach produktów to katastrofa wydajnościowa.

Jak indeks przyspiesza zapytanie?

Indeks w MySQL działa podobnie jak skorowidz na końcu książki. Zamiast czytać całą tabelę, baza danych sięga do struktury indeksu (najczęściej B-tree), odnajduje odpowiednie wskaźniki i pobiera tylko pasujące wiersze.

Przykład: Zapytanie SELECT * FROM products WHERE sku = 'PROD-12345' bez indeksu przeszukuje 500 000 wierszy. Z indeksem na kolumnie sku — odczytuje tylko 1 wiersz. Różnica w czasie wykonania? Z 4,2 sekundy do 0,001 sekundy.

Rodzaje indeksów w MySQL – który wybrać dla sklepu?

1. PRIMARY KEY (klucz główny)

Każda tabela powinna mieć klucz główny. W tabeli products to zwykle kolumna id z autoinkrementacją. MySQL automatycznie tworzy na niej unikalny indeks klastrowy, który fizycznie porządkuje dane na dysku.

CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    sku VARCHAR(50) NOT NULL,
    name VARCHAR(255) NOT NULL
);

2. UNIQUE INDEX (indeks unikalny)

Stosuj dla kolumn, które muszą być unikalne w całej tabeli — np. sku, ean, slug. Indeks unikalny przyspiesza wyszukiwanie i jednocześnie wymusza integralność danych.

ALTER TABLE products ADD UNIQUE INDEX idx_sku (sku);

3. INDEX (zwykły indeks, nieunikalny)

Używaj dla kolumn często występujących w klauzulach WHERE, JOIN i ORDER BY — np. category_id, manufacturer_id, status.

ALTER TABLE products ADD INDEX idx_category_status (category_id, status);

4. FULLTEXT INDEX (indeks pełnotekstowy)

Niezastąpiony przy wyszukiwarkach sklepowych. Pozwala na szybkie przeszukiwanie kolumn tekstowych (name, description) pod kątem słów kluczowych.

ALTER TABLE products ADD FULLTEXT INDEX ft_name_desc (name, description);

Zapytanie wykorzystujące indeks pełnotekstowy:

SELECT * FROM products 
WHERE MATCH(name, description) AGAINST('czerwona sukienka' IN BOOLEAN MODE);

5. COMPOSITE INDEX (indeks złożony, wielokolumnowy)

To najpotężniejsze narzędzie optymalizacji. Indeks złożony obejmuje kilka kolumn i przyspiesza zapytania filtrujące po wielu warunkach jednocześnie.

ALTER TABLE orders ADD INDEX idx_customer_date (customer_id, order_date);

⚠️ Uwaga: Kolejność kolumn w indeksie złożonym ma znaczenie. Indeks (A, B) przyspieszy zapytanie WHERE A = 1 AND B = 2 oraz WHERE A = 1, ale nie pomoże przy WHERE B = 2 bez warunku na A. To tzw. zasada lewego prefiksu.

Jak dobrać indeksy w sklepie internetowym? Praktyczne przykłady

Przykład 1: Filtrowanie produktów po kategorii i cenie

Klient wchodzi na stronę kategorii i sortuje produkty od najtańszych. Twoje zapytanie wygląda tak:

SELECT * FROM products 
WHERE category_id = 15 AND status = 'active' 
ORDER BY price ASC 
LIMIT 24;

Bez indeksu MySQL przeskanuje całą tabelę, nawet 200 000 wierszy. Z indeksem:

ALTER TABLE products ADD INDEX idx_cat_status_price (category_id, status, price);

Baza od razu trafia w interesujący ją zakres i pomija resztę. Czas wykonania spada z ~3,8 s do ~0,02 s.

Przykład 2: Wyszukiwarka sklepowa

Klient wpisuje „buty zimowe męskie”. Domyślne LIKE '%buty%' nie użyje indeksu i wykona full table scan. Rozwiązaniem jest FULLTEXT INDEX:

ALTER TABLE products ADD FULLTEXT INDEX ft_search (name, description, short_description);

SELECT * FROM products 
WHERE MATCH(name, description, short_description) 
AGAINST('+buty +zimowe +męskie' IN BOOLEAN MODE)
AND status = 'active'
LIMIT 24;

Wynik: z 8 sekund do 0,05 sekundy przy 500 000 produktów.

Przykład 3: Historia zamówień klienta

Klient loguje się i chce zobaczyć swoje zamówienia z ostatnich 30 dni:

SELECT * FROM orders 
WHERE customer_id = 2847 
AND order_date >= '2026-05-24' 
ORDER BY order_date DESC;

Indeks złożony rozwiązuje problem:

ALTER TABLE orders ADD INDEX idx_customer_date (customer_id, order_date);

Najczęstsze błędy przy tworzeniu indeksów

Błąd 1: Indeks na każdej kolumnie

Nadmiar indeksów spowalnia operacje INSERT, UPDATE i DELETE, bo MySQL musi aktualizować każdy indeks przy każdej modyfikacji danych. W sklepie z 50 000 produktów i indeksami na 15 kolumnach, aktualizacja ceny jednego produktu może dotykać 10 struktur indeksowych.

Zasada: Twórz indeks tylko dla kolumn realnie używanych w zapytaniach.

Błąd 2: Ignorowanie zasady lewego prefiksu

-- BŁĄD: Indeks (status, price) nie pomoże przy filtrowaniu tylko po price
ALTER TABLE products ADD INDEX idx_status_price (status, price);

-- DOBRZE: Jeśli filtrujesz często po samej cenie, indeks musi zaczynać się od price
ALTER TABLE products ADD INDEX idx_price (price);

Błąd 3: Indeksowanie kolumn z niską selektywnością

Kolumny typu status (tylko 2-3 wartości: active/inactive), is_deleted (0/1) czy gender (M/F) mają niską kardynalność. Indeks na takiej kolumnie często jest ignorowany przez optymalizator MySQL, bo szybciej jest przeskanować całą tabelę niż używać indeksu zwracającego 50% wierszy.

Zasada: Indeksuj kolumny, gdzie pojedyncza wartość nie przekracza ~5-10% wszystkich wierszy.

Błąd 4: Zapominanie o EXPLAIN

Nigdy nie zgaduj — zawsze sprawdzaj plan wykonania zapytania:

EXPLAIN SELECT * FROM products WHERE category_id = 15 AND status = 'active';

Kluczowe kolumny w wyniku EXPLAIN:

  • type: ALL = full table scan (źle), ref lub range = używa indeksu (dobrze), const = idealnie
  • key: nazwa użytego indeksu (NULL = brak indeksu)
  • rows: szacowana liczba przeszukiwanych wierszy (im mniej, tym lepiej)
  • Extra: Using filesort = MySQL sortuje ręcznie (źle), Using index = korzysta tylko z indeksu (idealnie)

Jak monitorować wydajność indeksów na produkcji?

1. Włącz Slow Query Log

Rejestruje zapytania trwające dłużej niż określony próg:

SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;  -- w sekundach
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

2. Analizuj logi narzędziem pt-query-digest (Percona Toolkit)

pt-query-digest /var/log/mysql/slow.log

Raport pokazuje zapytania posegregowane od najbardziej obciążających — od razu widzisz, które wymagają indeksu.

3. Sprawdzaj nieużywane indeksy

Od wersji MySQL 5.6 możesz sprawdzić, które indeksy nie były używane od restartu serwera:

SELECT * FROM sys.schema_unused_indexes;

Niepotrzebne indeksy usuń komendą:

ALTER TABLE products DROP INDEX idx_niepotrzebny;

Optymalizacja indeksów dla konkretnych platform e-commerce

WooCommerce (WordPress)

Najczęściej używane tabele i rekomendowane indeksy:

-- Przyspieszenie wyszukiwarki produktów
ALTER TABLE wp_postmeta ADD INDEX idx_meta_key_value (meta_key(50), meta_value(100));

-- Filtrowanie produktów po atrybutach
ALTER TABLE wp_woocommerce_order_items ADD INDEX idx_order_product (order_id, order_item_type);

PrestaShop

-- Optymalizacja listy produktów w kategorii
ALTER TABLE ps_product_shop ADD INDEX idx_cat_active_price (id_category_default, active, price);

-- Przyspieszenie wyszukiwarki
ALTER TABLE ps_product_lang ADD FULLTEXT INDEX ft_name (name);

Magento 2

Magento intensywnie korzysta z EAV (Entity-Attribute-Value), co generuje wiele JOIN-ów. Kluczowe indeksy:

-- Indeks dla cen produktów
ALTER TABLE catalog_product_index_price 
ADD INDEX idx_price_customer_group (price, customer_group_id, website_id);

Powiązany artykuł: Redis w e-commerce – Rozdzielenie Cache od Sesji. Cache i indeksy działają najlepiej, gdy są skonfigurowane razem.

FAQ — Najczęściej zadawane pytania o indeksy MySQL

Czy indeksy spowalniają zapis do bazy?

Tak. Każdy indeks musi być aktualizowany przy INSERT, UPDATE i DELETE. Przy tabeli z 10 indeksami, jeden UPDATE może wywołać 11 operacji dyskowych (1 na dane + 10 na indeksy). Dlatego indeksuj tylko kolumny, które realnie tego potrzebują.

Ile indeksów to za dużo?

W praktyce e-commerce: 5-8 indeksów na tabelę produktów to rozsądny limit. Tabela zamówień: 4-6. Wszystko zależy od specyfiki zapytań — zawsze decyduj na podstawie EXPLAIN i Slow Query Log, nie na wyczucie.

Jak sprawdzić, czy moje zapytanie używa indeksu?

EXPLAIN SELECT * FROM products WHERE sku = 'TEST-123';

Jeśli kolumna key w wyniku pokazuje nazwę indeksu (np. idx_sku) — indeks jest używany. Jeśli widzisz NULL — zapytanie wykonuje full table scan.

Czy indeks na VARCHAR(255) różni się od indeksu na INT?

Tak. Indeks na INT jest szybszy, zajmuje mniej miejsca w pamięci i na dysku. Dla kolumn VARCHAR rozważ indeks prefiksowy, np. INDEX idx_name (name(50)) — indeksuje tylko pierwsze 50 znaków, co wystarcza przy wyszukiwaniu.

Czy indeks przyspieszy ORDER BY?

Tak, pod warunkiem że kolumna z ORDER BY jest objęta indeksem (samodzielnie lub jako część indeksu złożonego) i kolejność sortowania zgadza się z kolejnością w indeksie. W przeciwnym razie zobaczysz Using filesort w EXPLAIN — czyli MySQL sortuje ręcznie, poza indeksem.

Checklista optymalizacji indeksów dla sklepu internetowego

  1. Włącz Slow Query Log z progiem 0,5–1 sekundy
  2. Uruchom pt-query-digest i znajdź 5 najwolniejszych zapytań
  3. Dla każdego zapytania wykonaj EXPLAIN i zidentyfikuj full table scany
  4. Dodaj indeksy na kolumnach z WHERE, JOIN i ORDER BY
  5. Sprawdź EXPLAIN ponownie — upewnij się, że MySQL używa nowych indeksów
  6. Usuń nieużywane indeksy (sys.schema_unused_indexes)
  7. Monitoruj przez tydzień — sprawdź, czy czasy zapytań się skróciły
  8. Powtarzaj proces co kwartał — dane i zapytania zmieniają się wraz z rozwojem sklepu

Podsumowanie: Indeksy MySQL to fundament wydajnego e-commerce

Poprawnie zaprojektowane indeksy potrafią skrócić czas zapytań z kilku sekund do milisekund. W sklepie internetowym, gdzie każda sekunda opóźnienia zmniejsza współczynnik konwersji o 7% (dane Google), to nie kwestia estetyki — to bezpośredni wpływ na przychód.

Pamiętaj o trzech złotych zasadach:

  • Indeksuj to, czego szukasz — kolumny w WHERE, JOIN, ORDER BY
  • Nie indeksuj wszystkiego — nadmiar spowalnia zapis
  • Zawsze sprawdzaj EXPLAIN — nie ufaj intuicji, ufaj danym

Wdróż powyższą checklistę w swoim sklepie, a zobaczysz różnicę w czasie ładowania stron jeszcze tego samego dnia.


Masz pytania o optymalizację MySQL? Zostaw komentarz poniżej. Potrzebujesz pomocy z konkretnym zapytaniem? Wklej swój EXPLAIN w komentarzu — pomogę Ci go zinterpretować.

Pobierz darmową checklistę: 10 kroków do szybszego sklepu internetowego →

← Powrot do wszystkich artykulow

// alert_systemowy

Widzisz podobne symptomy na swoim serwerze e-commerce? Nie czekaj na awarię w szczycie ruchu.

→ Przejdź do formularza i zgłoś serwer do audytu zerowego