Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kdyby/Redis

 Kdyby/Redis

Filip Procházka

April 01, 2014
Tweet

More Decks by Filip Procházka

Other Decks in Technology

Transcript

  1. Jaký je plán? - řekneme si co je Redis -

    co umí kdyby/redis - zprovozníme ho - profit
  2. Co je Redis? - nízkoúrovňová databáze - s datovými strukturami

    - se scriptováním v LUA - serializuje se na disk - jednovláknový Redis umí datové struktury od jednoduchých key:value, přes seznamy a hashe po seřazené sety. Je možné mu poslat script v LUA který evalne přímo nad databázi, tedy si ušetříte hromadu komunikace. Další killer feature oproti memcache je i serializace na disk, což z něj dělá perfektního kanditáta pro session.
  3. .. a umí ještě - databáze (forma namespace) - transakce

    (optimistic locking) - pub/sub - replikace
  4. První dva bottlenecky v Nette? session a journal Výchozí session

    file storage je super, ale od určité návštěvnostni zvládá s přehledem zabíjet stránku. Journal v Nette je velice jednoduchá databáze, do které se ukládá jaké mají data v cachi tagy a priority pro mazání. Journal není tak úplně bottleneck pokud nepoužíváte tagy, ovšem pokud je používáte, tak narazíte na problémy při větší zátěži, nebo když do něj chce zapisovat hodně dat.
  5. Na co je Redis super? - session - cache -

    je to přece databáze! Redis se dá použít nejenom na cachování a sessions, ale je to i plnohodnotná databáze a tedy do ní můžete přešunout i jiné části aplikace a zefektivnit ji tím.
  6. Instalace: Redis Redis: http://redis.io/download PHP Extension: http://pecl.php.net/package/redis Je potřeba nainstalovat

    nejenom samotnou databázi Redis, ale taky i PHP extension, abychom z rychlosti vytřískali maximum.
  7. Instalace: Kdyby/Redis $ composer require kdyby/redis:~3.0 app/config/config.neon extensions: redis: Kdyby\Redis\DI\RedisExtension

    Samozřejmostí je instalace Kdyby/Redis přes composer a compiler extension. Řada 3.0 je pro Nette 2.1 a dev.
  8. Session Výchozí PHP session storage stojí za úplný ho... uby!

    Největší dvě úzká hrdla jsou čištění starých session a limit počtu souborů na složku ve většine filesystémů. Obojí jde řešit, ale přoč tím ztrácet čas když máme možnost použít Redis.
  9. Session: nativní storage session.save_handler = redis session.save_path = "tcp://127.0.0.1:6379" zapíná

    se pomocí: redis: session: on Nativní storage využívá interního mechanismu v PHP na ukládání session a handleru který dodává phpredis (ten php extension). Důležité ovšem je, že tato forma ukládání session neblokuje a možná ani nikdy nebude, protože Redis sám o sobě nepodporuje zamykání.
  10. Session: emulovaný storage SessionHandlerInterface zapíná se pomocí: redis: session: {native:

    off} Kdyby/Redis ale obsahuje i vlastní implementaci, která využívá jiný PHP mechanismus na ukládání sessions. Proč psát vlastní? Protože tato implementace dokáže session zamykat, jako standardní filesystemová. Díky tomu se nám nebudou přepisovat a ztrácet data. Obě implementace jsou kompatibilní a lze je libovolně přepínat bez strachu ze ztráty dat.
  11. Cache: Caching\IStorage redis: storage: on journal: on Kdyby/Redis obsahuje oddělené

    implementace journalu a cache a je možné obě dvě používat samostatně, ale ve výsledku dává smysl hlavně použití obou zároveň.
  12. Cache: rychlost class BenchmarkFileStorage implements \Nette\Caching\IStorage { function __construct(\Nette\Caching\IStorage $storage);

    function read($key) { try { $time = microtime(TRUE); return $this->storage->read($key); } finally {$this->calls[]=microtime(TRUE)-$time;} } Večer před posobotou jsem se rozhodl změřit rozdíl rychlosti FS storage oproti Redisu abych mohl ohromit slečny v publiku a napsal jsem si vlastní storage dekorátor, kterým jsem plánoval měřit dobu strávenou v cachi.
  13. Cache: rychlost opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=12 opcache.max_accelerated_files=12000 opcache.validate_timestamps=0 opcache.fast_shutdown=1 opcache.save_comments=1 opcache.load_comments=1

    opcache.verbosity_level=2 ; opcache.revalidate_freq=60 Opcache ve výsledku udělala brutální rozdíl pro filesytem i redis, takže pro úplnost - tohle nastavení jsem použil
  14. Cache: rychlost Filesystem (SSD disk!) První pageload 2607 calls /

    452.5 ms / 0.17 ms Obyčejný pageload 188 calls / 26.8 ms / 0.14 ms Redis První pageload 2607 calls / 776.9 ms / 0.30 ms Obyčejný pageload 188 calls / 44.4 ms / 0.23 ms První načtení stránky generovalo hromadu cache a tedy i hromadu požadavků na Redis. Ovšem toto nejsou požadavky na Redis, to jsou jednotlivá volání metod storage, celkový čas strávený jejich voláním a průměr na jeden method call.
  15. Cache: jaktože je pomalejší?! - redis neumí zámky jako fs

    - zápis storage dělá více requestů - journál dělá více requestů Jsem stejně v šoku jako vy :) Jenže ono to není tak úplně jednoduché. Zaprvé, časy z předchozího slajdu jsou pro jeden jediný request na localhostu a tedy to není úplně vypovídající. Filesystem totiž neškáluje zdaleka tak dobře jako Redis a s nárůstem zátěže se rychlost strovná a Redis postupně získá náskok, protože ten s množstvím requestů téměř vůbec nezpomaluje a horní limit je někde kolem půl milionu GETů za vteřinu. Redis neumí zamykat a tedy musím zámky emulovat. Emulování zámků se dělá pomocí http://redis.io/commands/setnx a lepší řešení jsem ani po hodinách studia nenašel. Když se zapisuje do storage, musí se tedy nejdříve získat zámek, potom se zapíše a ještě když ukládáme nějaké tagy ke klíčům tak to dělá další requesty kvůli journalu. Strávili jsme s @matej_21 několik bezesných nocí optimalizací journalu i storage až na dřeň plošných obvodů a pokud si myslíte že to zvládnete lépe, budu strašně rád když se zapojíte.
  16. Cache: na produkci 18 ms / rq Newrelic nám tvrdí

    že těch ~ 200 požadavů na Redis (tohle je jiné číslo než počet method calls storage) trvá průměrně 18ms na http request. Not bad huh?
  17. Cache: na produkci FS cache Redis Redis Zkoušel jsem Redis

    storage vypnout, ale bohužel v nočních hodninách už je ten rozdíl neznatelný. V poledne bych si naopak něco takového nedovolil.
  18. Cache: jak probíhá zápis - získám zámek (loop (setnx;usleep)) -

    zapíšu - uvolním zámek Zápis do cache funguje tak, že nejprve získám zámek cyklickým voláním SETNX na klíč se suffixem ‘:lock’ oproti klíči který chci zamknout. Platnost zámku jde samozřejmě nastavit na libovolný počet vteřin. Následně zapíšu data a zámek uvolním.
  19. Journál: jak probíhá zápis - smažu předcházející data pro klíč

    - zapíšu (rq ~= klíče*tagy + priorita) - uvolním zámek Do journalu se zapisuje trošku složitěji. Nejprve musíme smazat stará data pro daný klíč, tedy odmazat tagy a následně z tagů odmazat klíč, případně ještě další request na priority. Potom to samé udělám naopak, tedy spáruji jednotlivé tagy s klíčem a klíč s tagy, případně zapíšu prioritu.
  20. Journál: jak to dělá filejournal? - zamkne journal - celý

    journal načte do paměti - změní co potřebuje - zapíše journal Oproti tomu filejournal zamkne celý soubor ve kterém jsou všechna metadata, načte ho do paměti, pomění co potřebuje a opět zapíše. Jinak to totiž udělat ani nejde, pokud chceme mít atomický zápis. Tento způsob je samozřejmě skvělý, ale pouze do určitého počtu http požadavků.
  21. Journál: jak to dělá filejournal? - FileJournal::NODE_SIZE - není možné

    mít pro jeden klíč víc než X tagů a pro jeden tag víc než X klíčů Bohužel tady narážíme na limity filejournalu, tedy že se skládá z jakýchsi datových bloků, do kterých se ukládají data a byť je rozumně naddimenzovaný, i tak se může stát že máte moc tagů pro nějaký klíč, nebo moc klíčů pro nějaký tag a zkrátka nebude možné data uložit. Redis journal je omezen pouze velikostí ramky.
  22. Cache: smazání klíče - smaže klíče Tady je to naštěstí

    velice jednoduché, stačí jeden request a data jsou fuč :)
  23. Cache: expirace klíče Filestorage ho musí načíst, ověřit a “ručně”

    smazat. V Redisu automaticky expiruje, jako by tam nikdy nebyl :) Další skvělá feature redis cache storage je automatická expirace klíčů na úrovni databáze. Filestorage je po sobě musí klízet když zjistí že data mají expirovat, v Redisu prostě zmizí.
  24. Cache: mazání podle tagu - získá seznam klíčů pro tag

    (journal) - smazat tagy z klíčů (journal) - smazat klíče z tagů (journal) - vrátí seznam klíčů na smazání - smazat nalezené klíče (cache) Tohle je asi největší špek jaký jsme v celém journalu řešili. Normální filestorage a journal fungují tak, že storage dá journalu tag (případně jiné pravidlo jak cache mazat) a zeptá se jaké klíče ten tag mají a které teda má mazat. Journal to přechroustá a vrátí list klíčů. Cache následně maže data.
  25. Cache: mazání podle tagu počet requestů odpovídá velikosti dat 10

    000 rq? 100 000 rq? 1 000 000 rq? Co byste řekli, kolik musí udělat requestů algoritmus popsaný na minulém slide? Pokud byste se nebáli tipnout i ta šílená čísla s hodně nulami nebyli byste daleko od pravdy, protože pokud máte u jednoho tagu tisíce klíčů cache, může to dát redisu pořádně pokouřit.
  26. Cache: mazání podle tagu 1 request = eval lua scriptu

    Ovšem s tím se odmítám spokojit! Jak jsme si řekli, Redis umí scriptovat v Lua a proto jsem napsal mazání pomocí tagů journalu jako Lua script, který se jednoduše pošle databázi s argumentem (jméno tagu, priorita), databáze evalne script a ten najde všechny klíče, promaže všechna metadata a pokud použiváte redis cache storage i journal zároveň, rovnou zkusí klíče z databáze smazat. Ušetří se tedy vracení seznamu klíčů a mazání “z venku”.
  27. Optimalizace: unixové sockety “The typical latency of a 1 GBits/s

    network is about 200 us, while the latency with a Unix domain socket can be as low as 30 us.” Lokální síť mezi dvěma VPS je samozřejmě brutálně rychlá a když posíláte jenom pár requestů tak ten rozdíl nejspíš nepocítíte, ale když sypete do Redisu na každý http request stovky příkazů tak už se vyplatí zamyslet se nad použitím lokálních instací redisu na cachování pro každý http server (myslím tím VPS, která je určená ke zpracování http requestů a běží na ní PHP).
  28. Optimalizace - zvlášť instance na session a na cache -

    vypnout serializaci cache na disk - vypnout zamykání cache pro zápis Kdyby/Redis je sice chytrý a cache i session prefixuje a tedy se nikdy nepomýchají, ale výborné je rozdělit je do dvou různých databází. Jak jsme si řekli, data v redisu se mohou dělit na databáze a tedy při deployi můžete jednoduše zavolat `redis-cli -n 0 flushdb` a smazat tak databázi s cachí a session zůstane nedotčena. Pokud to chcete povýšit, místo použití databází můžete nastartovat dvě různé instance redisu a jednu použít na session a jednu na cache. Díky tomu budete moci vypnout ukládání cache na disk a tedy zase trochu ulehčíte Redisu ve špičkách. Když jsme měli problémy s výkonem, protože jsme hodně experimentovali, zkoušeli jsme i vypnout zamykání cache pro write, ale výsledkem bylo jenom to, že se cache generovala klidně 100x dokud ji ten první request, co ji generovat začal, konečně nezapsal a tedy se výkon logicky zhoršil.
  29. Optimalizace: cache nestíhá :( Vypnutí generování cache, nebo naopak když

    generujete cache strašně moc v jeden moment může vypadat i takto. Naprostá většina toho nárustu je spůsobená sleepy v cyklech a opakovné dotazování Redisu jestli už se zámek uvolnil. Stroj tedy ve skutečnosti není zase o tolik vytíženější, jenom mu déle trvá zpracovat http requesty.
  30. Optimalizace: sharding Více instancí => více vláken! + persistentní spojení

    No a když už vám návštěvnost naroste takovým způsobem že už nic nepomáhá a komunikace s Redisem se ukáže jako úzké hrdlo, je na řadě zvážit sharding. Protože jak jsme si řekli na začátku, Redis je jednovláknový a tedy tím že nastartujete více instancí (třeb 16, nebo klidně 64) získáte více vláken :)
  31. Optimalizace: sharding - https://github.com/twitter/twemproxy - kdyby/redis (maybe?) Asi nejpoužívanější nástroj

    na shardování Redisu je twemproxy od Twitteru, jenže ta je navržená tak, že by asi úplně dobře nefungovala při invalidaci cache pomocí našeho LUA scriptu, tedy tu bohužel nemůžu pro kombinaci s Kdyby/Redis doporučit. Ale nenechám vás na holičkách, mám napsané vlastní klientské shardování. Zatím ovšem není v masteru, ale v samostatné větvi a budu rád když ji se mnou zkusíte otestovat, ale opatrně! :)
  32. A závěr? A vyplatí se teda (Kdyby/)Redis? Jak jsme si

    ukázali, naše konkrétní implementace storage umí bohužel i nemile překvapit.
  33. Vyplatí se pro Session: vždy Journal: vždy Ovšem i kdyby

    byla klidně ještě o polovinu pomalejší, pořád se ve 100% případů vyplatí použít pro session. A pokud tagujete cache tak v ten moment se journal a cache vyplatí i vám.