Databázová architektura pro velké produktové katalogy
Do desítek tisíc produktů unese e-shop skoro jakákoli databáze. Pak začnou rozhodovat datový model, indexy a to, co se vůbec má ptát databáze. Ukážeme, jak velké katalogy stavíme.
Katalog s pěti tisíci produkty unese skoro jakákoli databáze na skoro jakémkoli hostingu. Se stovkami tisíc položek, desítkami parametrů a několika jazyky je to jiná hra. Výpis kategorie s filtry se začne načítat vteřiny, import od dodavatele zamkne tabulky a export feedu pro srovnávače srazí web ve špičce.
Většina těchto problémů nevzniká tím, že by databáze „nestačila“. Vzniká návrhem: jak jsou uložené parametry, co je zaindexované a jaké dotazy vůbec posíláte do hlavní databáze. V článku ukážeme principy, podle kterých velké katalogy navrhujeme a opravujeme.
Kde velké katalogy narážejí
Typické příznaky, se kterými za námi e-shopy přicházejí:
- Výpis kategorie s několika zaškrtnutými filtry se načítá dlouho a zhoršuje Core Web Vitals.
- Vyhledávání nenajde produkt při překlepu nebo bez diakritiky.
- Import ceníku nebo skladu od dodavatele běží hodiny a web mezitím zpomalí.
- Export XML feedů zatěžuje databázi stejně jako tisíce návštěvníků.
- Administrace je pomalá, protože přehledy počítají souhrny nad celou tabulkou objednávek.
Každý z těchto příznaků má jinou příčinu. Proto začínáme měřením: log pomalých dotazů, plán dotazu (EXPLAIN) a zjištění, kdy a co databázi zatěžuje.
Parametry produktů: EAV, nebo JSON
Produkty v různých kategoriích mají různé parametry. Televize má úhlopříčku, tričko velikost a materiál. Klasická tabulka se sloupcem pro každý parametr tu nefunguje. Používají se dva hlavní přístupy.
EAV (entity–attribute–value). Každá hodnota parametru je samostatný řádek: produkt, atribut, hodnota. Model je flexibilní a nové atributy přidáte bez změny schématu. Používá ho například Magento, které má hodnoty navíc rozdělené do tabulek podle datového typu. Nevýhoda: sestavit jeden produkt nebo filtrovat přes pět atributů znamená mnoho spojení tabulek. Proto EAV systémy data často předpočítávají do plochých indexových tabulek.
JSON sloupec. Parametry jsou uložené jako dokument v jednom sloupci. Načíst celý produkt je jeden dotaz. Moderní databáze JSON umí i indexovat. PostgreSQL má typ jsonb a GIN indexy, s třídou jsonb_path_ops optimalizovanou pro dotazy na obsah (@>). MySQL od verze 8.0.17 podporuje multi-valued indexy nad poli v JSON, které se použijí u MEMBER OF(), JSON_CONTAINS() a JSON_OVERLAPS().
| Kritérium | EAV | JSON sloupec |
|---|---|---|
| Přidání nového parametru | bez změny schématu | bez změny schématu |
| Validace typů a hodnot | v databázi (tabulky podle typu) | hlavně v aplikaci |
| Načtení celého produktu | mnoho spojení nebo předpočítaný index | jeden řádek |
| Filtr přes více parametrů | pomalý bez předpočítaných tabulek | použitelný s indexy, limity u složitých dotazů |
| Hromadné změny jednoho atributu | jednoduché | přepis dokumentů |
| Vhodné pro | složité katalogy s přísnou správou atributů | aplikace na míru, menší počet filtrů |
V praxi často kombinujeme. Zdroj pravdy o atributech je v PIM nebo v normalizované databázi. Pro zobrazení a filtrování se data denormalizují do vyhledávače nebo do předpočítaných tabulek.
Indexy: méně hádání, víc měření
Chybějící index je nejčastější a nejlevněji opravitelná příčina pomalosti. Opačný extrém je ale taky problém. Každý index zpomaluje zápis, což u katalogu s častými importy cen a skladu bolí.
Principy, které se nám osvědčily:
- Indexujte podle skutečných dotazů, ne podle sloupců. Vyjděte z logu pomalých dotazů.
- Složené indexy řaďte podle použití. Index
(kategorie, dostupnost, cena)pomůže dotazu, který filtruje kategorii a dostupnost a řadí podle ceny. - Pozor na funkce ve WHERE. Dotaz, který na sloupec aplikuje funkci, běžný index nevyužije.
- Nepoužívejte fulltext přes `LIKE '%slovo%'`. Takový dotaz index nevyužije a prochází celou tabulku.
- Stránkujte přes klíč, ne přes vysoký OFFSET. Hluboké stránkování s velkým OFFSETem nutí databázi číst a zahazovat tisíce řádků.
Vyhledávání a filtry patří do vyhledávače
Od určité velikosti katalogu přestává dávat smysl filtrovat a vyhledávat přímo v relační databázi. Fazetové filtry (počty produktů u každé hodnoty parametru), tolerance překlepů, skloňování a řazení podle relevance jsou úloha pro vyhledávač.
Nejpoužívanější jsou Elasticsearch a OpenSearch. OpenSearch vznikl v roce 2021 jako fork Elasticsearch po změně jeho licence. Od září 2024 ho spravuje OpenSearch Software Foundation pod Linux Foundation. Elastic ve stejném měsíci přidal k Elasticsearch jako další licenční možnost AGPLv3. Pro e-shop jsou oba nástroje funkčně srovnatelné. Rozhoduje spíš ekosystém a podpora platformy. Magento (Adobe Commerce) od verze 2.4.8 podle dokumentace Adobe podporuje už jen OpenSearch.
Jak to funguje: produkty se z databáze indexují do vyhledávače. Výpisy kategorií, filtry a vyhledávání čtou z vyhledávače. Detail produktu a košík čtou z databáze. Změny se do indexu propisují průběžně, ideálně přes frontu. Popisujeme to v článku Fronty a asynchronní zpracování v ecommerce.
Pro češtinu počítejte s nastavením analyzátoru: odstranění diakritiky, synonyma a skloňování. Bez toho vyhledávač najde „boty“, ale ne „botu“. Dalším krokem může být sémantické vyhledávání, které hledá podle významu dotazu.
Read repliky: oddělte čtení od zápisu
Replika je kopie hlavní databáze, která přebírá změny. Posíláte na ni čtecí dotazy, které nemusí mít úplně aktuální data: reporty, exporty feedů, analytiku, synchronizaci do dalších systémů. Hlavní databáze se pak věnuje objednávkám a zápisům.
Podstatné omezení: replikace je obvykle asynchronní. Dokumentace Amazon RDS to uvádí výslovně a doporučuje, aby aplikace, které potřebují hned číst, co zapsaly, četly z hlavní databáze. Pro e-shop to znamená:
- Košík, checkout, stav objednávky a zákaznický účet čtěte z hlavní databáze.
- Na repliku posílejte exporty, reporty a výpisy, kde nevadí zpoždění v řádu sekund.
- Sledujte zpoždění repliky (replica lag) a nastavte upozornění.
Cache ve vrstvách
Nejrychlejší dotaz je ten, který do databáze vůbec nedojde. U velkých katalogů pracujeme s několika vrstvami cache:
| Vrstva | Co ukládá | Typický nástroj |
|---|---|---|
| CDN | obrázky, statické soubory, případně celé stránky | CDN poskytovatele |
| Full-page cache | hotové HTML stránky pro nepřihlášené | Varnish, cache platformy |
| Aplikační cache | výsledky dotazů, konfigurace, strom kategorií | Redis nebo Valkey |
| Databázová cache | často čtená data v paměti serveru | buffer pool databáze |
Největší problém cache není nastavení, ale zneplatnění. Když se změní cena, musí zmizet všechny stránky, kde byla vidět. Proto navrhujeme cache spolu s tím, jak se do e-shopu dostávají změny z importů a z ERP.
Jak postupovat prakticky
- Změřte. Zapněte log pomalých dotazů, zjistěte, které stránky a úlohy databázi zatěžují a kdy.
- Opravte indexy a nejhorší dotazy. Často to vyřeší velkou část problémů bez změny architektury.
- Přesuňte importy mimo špičku a do dávek. Hromadné zápisy po menších dávkách zamykají méně. Víc o ETL procesech najdete ve znalostní bázi.
- Nasaďte vyhledávač pro výpisy, filtry a vyhledávání.
- Oddělte čtení exportů a reportů na repliku.
- Doplňte cache a navrhněte její zneplatnění.
- Až pak zvažujte změnu datového modelu. Je to nejdražší krok a měl by stát na datech z měření.
Pokud váš katalog roste rychleji než výkon e-shopu, pomůžeme s analýzou i realizací. Návrh databáze a vyhledávání děláme v rámci programování na míru, hromadné importy a exporty v rámci služby importy a exporty. Širší pohled na růst katalogu dává článek Škálování e-shopu s rostoucím katalogem.
Potřebujete s tím pomoct?