Esenciálnou a nevyhnutnou časťou úspechu webového produktu je jeho výkonnosť. Jednou zo zložiek výkonnosti je reakcia aplikácie na požiadavku klienta a následná odpoveď (request vs response).

Reakcia používateľa na dĺžku trvania požiadavky

  • do 0.1 sekundy má používateľ pocit, že všetko reaguje okamžite
  • do 1 sekundy je limit kedy ešte tok myšlienok používateľa ostáva neprerušený aj keď už začína cítiť, že systém nereaguje úplne okamžite
  • do 10 sekúnd už používateľ stráca pozornosť. Táto hodnota je už neakceptovateľná

Z uvedených údajov vidíme, že naša snaha je optimalizovať obsluhu požiadavky do jednej desatiny sekundy. Všetko nad túto hranicu je pri moderných aplikáciach pre používateľa neakceptovateľný. Ako sa vraví “každá sekunda (desatina sekundy) sa počíta”. Desatina sekundy môže rozhodnúť o tom, či sa zákazník na stránku vráti alebo nie.

Pri optimalizácii plati zlaté pravidlo : “Čím viacej procesov eliminujeme na strane servera, tým budeme mať rýchlejšiu odpoveď”. Jednou z možností je implementovanie kešovania (caching data). My si ukážeme ako to vyriešiť v Djangu cez Redis.

Čo je Redis

Redis je pamäťová databáza (in-memory data structure store), ktorá môže byť použitá ako kešovací mechanizmus (caching engine). Tým, že si Redis ukladá dáta do RAM pamäti, vie byť požiadavka výberu dát rýchla. Túto vlastnosť teda využijeme v našom django projekte. Existuje samozrejme veľa iných cache riešení ako napríklad použitie Memcached , my ostaneme pri Redise.

Naša aplikácia bude požívať knižnice django (samotný projekt), django-redis (ovládače na redis databázu) a samotný Redis, ktorý použijeme ako plugin v heroku cloud platforme. Samozrejme Redis sa dá použiť ako samostatne nainštalovaný server. My šetríme čas a prostriedky, ktorý by sme museli investovať do udržiavania a správy Redis serverov, preto radšej využívame škálovateľnosť heroku platformy.

Inštalácia ovládačov Redis pre Django pip install django-redis pip freeze > requirements.txt

V nasledujúcej fáze pridáme Heroku Redis plugin cez dashboard Heroku. Ako rozbehať Django aplikáciu a pridať plugin v prostredí Heroku nájdete v článku Ako nasadiť Django na Heroku.

Po tom ako sa pridal plugin si kliknutím na plugin pozrieme credentials v ktorom máme všetky prístupové údaje k databáze. Tie použijeme v setting.py. Odporúčam nevkladať priamo credentials do settings.py ale načítať ich cez systémové premenné heroku. Tie sa nám automaticky v tomto prostredí nastavili pod konštantou REDIS_URL

nastavenia v settings.py REDIS_URL = os.environ.get('REDIS_URL') CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": REDIS_URL, "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient" }, "KEY_PREFIX": "example" } }

Kešovanie v Djangu je nastavené a plugin pripravený. Po spustení aplikácie sa nám však nič nestane. Ešte sme Djangu nepovedali, kde chceme, aby bol uplatnený caching.

Do našeho projektu uplatníme kešovanie šablón (templates). To znamená, že pri požiadavke na vygenerovanie šablóny server nebude generovať šablónu zakaždým keď to bude potrebné, ale vyberie už vygenerovanú šablónu s cache Redisu. V druhom prípade budeme kešovať query na našu databázu. To znamená, že pri požiadavke na zobrazenie všetkých produktov z databázy nebudeme pri volanom requeste robiť dotaz na databázu, ale vytiahneme dáta z cache. Je ale dôležité si uvedomiť kedy a kde implementujeme kešovanie. Nie vždy chceme dostať “staré dáta”. Napríklad komentáre od používateľov by mali byť vždy aktuálne.

Vhodní kandidáti na kešovanie budú :

  • dáta, ktoré sú stále rovnaké alebo sa menia len zriedka
  • stránky na ktoré chodí veľmi veľa používateľov
  • stránky, ktoré vyžadujú veľa dopytov na databázu alebo ich výpočet je náročný

Ako funguje reálne kešovanie a ako ju nastaviť v Djangu

Zoberme si situáciu, že 1000 používateľov by chcelo pozrieť nejaký náš napísaný článok a keďže by to bola zaujímavá téma, všetci by sa na Vaše stránky prihlásili naraz. Bez kešovania by aplikácia musela 1000x sa pripojiť na databázu, vyskladať potrebné dáta, vygenerovať html šablónu a poslať odpoveď naspäť. To 1000x dookola. To nehovorím o tom, že v rámci jednej požiadavky môže aplikácia vyberať z databázy dáta, ktoré boli poskladané z viacerých databázových dotazov. Aplikácia by určite nereagovala svižne. Implementovaním Redisu Django pred spracovaním požiadavky klienta skontroluje kľúč v databáze Redisu, či už neexistuje z minulosti rovnaká požiadavka, ktorá sa uložila do RAM pamäte Redisu. Ak áno tak iba vyberie túto zapísanú odpoveď a pošle naspäť klientovi. Ak nie, tak spracuje požiadavku a uloží do pamäte. Čas ako dlho sa majú nechať uložené výsledky v Redise sa určuje parametrom CACHE_TTL.

nastavenia v settings.py # Ak vynecháme túto properties tak prednastavený čas je 5 minút. Čas sa zadáva v sekundách. CACHE_TTL = 60 * 15
nastavenia vo views.py from django.conf import settings from django.core.cache.backends.base import DEFAULT_TIMEOUT from django.shortcuts import render from django.views.decorators.cache import cache_page from cookbook.services import get_recipes   CACHE_TTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)   # Dekorátorom definujeme, že view sa bude kešovať pre dobu 15 minút @cache_page(CACHE_TTL) def recipes_view(request): return render(request, 'cookbook/recipes.html', { 'recipes': get_recipes() })

To je všetko čo je potrebné spraviť, aby Django aplikácia kešovala views. Viacej o tejto problematike nájdete na oficiálnych dokumentačných stránkach projektu Django Caching


Michal Kalman
Michal Kalman
Softvérový vývojár

Full-stack developer a podnikateľ zameriavajúci sa na platformu Java, Python. Mám dlhoročné profesionálne skúsenosti s vývojom Java/JavaEE aplikácií. Som zakladateľ firmy Morione, ktorá sa venuje návrhom a realizáciou škálovateľných webových aplikácií. Podporujem a vyvíjam viaceré startupy. Vo voľnom čase cestujem po svete, behám po horách, píšem blogy a tvorím kreatívne videá.