Co je caching?
Caching znamená dočasné ukládání dat, která aplikace pravděpodobně bude znovu potřebovat.
Představme si e-shop, na kterém tisíce uživatelů opakovaně otevírají stejnou produktovou stránku. Aplikace by mohla při každém otevření stránky posílat dotaz do databáze. Pokud se ale informace o produktu nezměnily, není příliš důvod provádět stejný databázový dotaz znovu a znovu.
Místo toho může aplikace data jednou načíst z databáze, uložit je do cache, například v Redisu, a další požadavky obsloužit přímo z cache.
Základní tok vypadá takto:
První požadavek: aplikace → databáze → Redis → uživatel
Další požadavky: aplikace → Redis → uživatel
Výsledkem může být méně databázových dotazů, nižší zatížení a rychlejší odezva.
Proč databáze nemusí být vždy úzkým hrdlem
Když se aplikace zpomalí, databáze bývá jednou z prvních věcí, na kterou se vývojáři zaměřují. Někdy je problém skutečně tam. Ne vždy tomu tak ale je.
Databázový dotaz může být perfektně optimalizovaný, a přesto být nákladný, pokud se spouští tisíckrát.
Představme si například dashboard, který počítá statistiky na základě velkého množství historických dat. Samotný dotaz může trvat třeba jen 200 milisekund. To na první pohled nezní jako velký problém. Pokud ale stovky uživatelů požadují stejný výpočet každou minutu, databáze stále dokola provádí stejnou práci.
Caching může pomoci tím, že se výsledek vypočítá jednou a následně se znovu použije pro další požadavky.
To ale neznamená, že bychom měli caching automaticky přidat pokaždé, když se aplikace zpomalí. Než do architektury přidáme další vrstvu, je důležité zjistit, kde se skutečný problém nachází.
Někdy je správným řešením databázový index. Jindy lepší dotaz. A někdy je potřeba optimalizovat samotnou logiku aplikace.
Redis vs. databáze
Redis a relační databáze, jako je PostgreSQL, řeší různé problémy.
PostgreSQL je obvykle zdrojem pravdivých a trvalých dat (source of truth). Je navržený pro ukládání persistentních dat, práci s transakcemi, vztahy mezi entitami a komplexní dotazy.
Redis je databáze pracující v paměti (in-memory data store). Jeho hlavní předností je rychlost, díky čemuž se hodí pro data, ke kterým se často přistupuje, jsou dočasná nebo je jejich opakované získávání náročné.
Typické využití Redisu zahrnuje:
- výsledky náročných databázových dotazů
- uživatelské relace
- dočasné tokeny
- rate limiting
- čítače
- odpovědi API
- často načítaná data, která se mění jen zřídka
Typická architektura proto může vypadat takto:
Aplikace → Redis → PostgreSQL
PostgreSQL zůstává spolehlivým zdrojem dat, zatímco Redis funguje jako rychlá vrstva mezi aplikací a databází.
Cache-Aside Pattern
Jednou z nejčastěji používaných strategií cachingu je tzv. Cache-Aside Pattern.
Princip je jednoduchý: aplikace nejprve zkontroluje, zda požadovaná data už nejsou v cache.
Pokud tam jsou, aplikace je okamžitě použije.
Pokud tam nejsou, aplikace je načte z databáze, uloží do Redisu a následně je vrátí uživateli.
Proces vypadá takto:
- Aplikace přijme požadavek.
- Zkontroluje Redis, zda obsahuje požadovaná data.
- Pokud data existují, Redis je vrátí.
- Pokud neexistují, aplikace se dotáže PostgreSQL.
- Výsledek uloží do Redisu.
- Aplikace vrátí výsledek uživateli.
Při dalším požadavku tak mohou být data obsloužena přímo z Redisu.
Tento přístup je obzvlášť užitečný tehdy, když jsou stejná data požadována často, ale nemění se příliš často.
TTL: Jak dlouho by měla data zůstat v cache?
Data uložená v cache nemusí zůstat v paměti navždy.
Právě zde přichází na řadu TTL (Time To Live). TTL určuje, jak dlouho zůstane uložená hodnota platná, než automaticky expiruje.
Můžete například uložit produkt do Redisu s TTL pěti minut. Po uplynutí těchto pěti minut hodnota v cache expiruje a aplikace může načíst aktuální data z databáze.
Správná hodnota TTL závisí na typu dat.
U dat, která se často mění, může dávat smysl krátké TTL. U dat, která se mění jen zřídka, může být cache platná výrazně déle.
Důležitá otázka proto není jen:
„Jak dlouho můžeme tato data cachovat?“
Ale spíše:
„Jak dlouho mohou být tato data bezpečně mírně zastaralá?“

Nejtěžší část? Invalidace cache
V softwarovém vývoji existuje známé rčení:
„There are only two hard things in Computer Science: cache invalidation and naming things.“
A invalidace cache skutečně může být komplikovaná.
Představme si, že cena produktu je uložená v Redisu. Cena se v PostgreSQL změní, ale stará hodnota zůstane v cache. Uživatelé tak mohou stále vidět neaktuální cenu, dokud cache neexpiruje.
Existuje několik způsobů, jak tento problém řešit.
Jednou možností je smazat záznam z cache pokaždé, když se změní příslušná data.
Další možností je aktualizovat hodnotu v cache současně se změnou hodnoty v databázi.
Třetí možností je spolehnout se na TTL a přijmout skutečnost, že hodnota v cache může být po omezenou dobu mírně zastaralá.
Správná strategie závisí na tom, jak důležitá je aktuálnost dat. Několik sekund zpoždění může být u statistiky v dashboardu naprosto přijatelné, ale například u částky platby už rozhodně ne.
Co se může pokazit?
Caching zlepšuje výkon, ale zároveň přináší další vrstvu komplexity.
Jedním z běžných problémů jsou zastaralá data. Cache může obsahovat starší verzi informací než databáze.
Dalším problémem je tzv. cache stampede. Pokud expiruje často používaná hodnota v cache a ve stejnou chvíli přijde velké množství požadavků, všechny se mohou pokusit získat stejná data z databáze současně.
Je také potřeba myslet na paměť. Redis ukládá data v operační paměti a ta není neomezená. Cachovat všechno proto obvykle není dobrá strategie.
Caching navíc zvyšuje provozní komplexitu. Přibývá další služba, kterou je potřeba monitorovat, další potenciální bod selhání a další část architektury, které musí vývojáři rozumět.
Kdy Redis nepoužívat?
Redis není automaticky odpovědí na každý problém s výkonem.
Pokud je vaše aplikace malá a databáze bez problémů zvládá současnou zátěž, může Redis pouze zbytečně přidat další komplexitu.
Pokud je například dotaz pomalý proto, že mu chybí index nebo je špatně napsaný, cachování jeho výsledku může pouze zakrýt skutečný problém.
Caching také nemusí být vhodný v situacích, kdy data musí být vždy zcela aktuální nebo se mění tak často, že uložené hodnoty v cache téměř nedávají smysl.
Prvním krokem by proto vždy mělo být měření.
Nejdříve zjistěte, kde se skutečně nachází úzké hrdlo. Teprve potom rozhodujte, zda caching tento problém dokáže vyřešit.
Redis + Django + PostgreSQL
Redis se velmi dobře hodí do aplikací postavených na Django a PostgreSQL.
Django už nabízí vlastní caching framework, který lze propojit s Redisem. Typická architektura může vypadat například takto:
Django → Redis → PostgreSQL
Aplikace nejprve zkontroluje Redis, zda obsahuje často požadovaná data. Pokud tam nejsou, Django se dotáže PostgreSQL, uloží výsledek do Redisu a vrátí ho uživateli.
To může být obzvlášť užitečné u aplikací, kde výrazně převažují čtecí operace nad zápisy.
Pokud například tisíce uživatelů opakovaně požadují stejné, relativně stabilní informace, caching může zabránit tomu, aby PostgreSQL prováděl stejnou práci stále dokola.
Cache není náhradou za dobrou architekturu
Redis není magické tlačítko na zrychlení aplikace.
Než caching zavedete, je potřeba pochopit, co aplikaci skutečně zpomaluje. Řešením může být lepší databázový index, efektivnější dotaz, optimalizace aplikační logiky nebo jiný architektonický přístup.
Caching dává největší smysl tehdy, když je získání nebo výpočet dat nákladný a tato data jsou následně opakovaně požadována.
Nejdůležitější otázka proto zní:
Která data se vyplatí načíst jednou a následně je použít vícekrát?
Právě zde může Redis přinést skutečný rozdíl – ne tím, že nahradí databázi, ale tím, že sníží počet situací, kdy aplikace musí databázi žádat, aby opakovaně prováděla stejnou práci.