REDIS — tipy na performance ladění a konfiguraci

Petr Jůza
OpenWise Tech blog
Published in
8 min readJun 8, 2020

Představovat REDIS mě přijde zbytečné, proto uvedu jen kvůli kontextu, že my REDIS používáme nejvíce jako cache, tedy z pohledu aplikace pro rychlý přístup k dříve uloženým datům. Není nutné se omezovat jen na tuto funkčnost, možnosti REDISu jsou mnohem větší. Co se mě osobně nejvíce na REDISu líbí, je ta celková jednoduchost, hodně vysoká výkonnost a škálovatelnost — na první pohled nezaujme výčtem funkcí, umí toho méně než konkurence, ale to co umí, tak umí perfektně.

Tento článek nebude souhrným textem, ale spíše zdroj tipů a návodů, které nám při ladění performance přijdou užitečné a sami je používáme. Většina věcí zde uvedených je poplatných všem verzím REDISu, nicméně je potřeba uvést, že my jsme konkrétně ladili verzi 3.0.7. (ano, tato verze je již několik let stará a chystáme upgrade, nicméně performance problémy bylo nutné vyřešit už teď).

Souhrnných článků na stejné téma již existuje spousta, na konci článku příkládám několik odkazů, které mě zaujali.

Pokud vás zajímá co vše REDIS umí a kde pro vás může být užitečný, pak se vám může hodit má prezentace z interního meetupu u nás ve firmě: https://www.linkedin.com/feed/update/urn:li:activity:6663879207136952320

Doporučení pro sběr provozních dat

Pro sběr dat z chování REDISu je vhodné mít přípravenou sadu příkazů nebo přímo skript, který se rychle spustí a posbírá vše důležité. Úplně nejlepší samozřejmě je, pokud je možné mít na produkci trvalý sběr dat v pravidelných intervalech. Performance overhead s tím spojený je zcela minimální, obavy jsou často zbytečné. Nejvíce se nám osvědčila tato sada:

  • sledování vývoje počtu klíčů, paměti, requestů a připojení, vše navíc v interaktivním módu (defaultně se spouští každou sekundu). Užitečné pro zjištění celkového stavu REDISu.

redis-cli --stat

  • zjištění nejpomalejších příkazů. Užitečné pro nalezení konkrétních volání, kde je největší problém (pozn. výpis je ovlivněn konf. parametrem slowlog-log-slower-than, který v defaultním nastavení loguje všechny commandy trvající déle než 10ms.). Výpis obsahuje záznamy od startu REDISu nebo od resetování — SLOWLOG RESET.

SLOWLOG GET 20

  • sledování latence. REDIS obsahuje interní monitoring latencí, který je skvělým nástrojem pro výkonnostní ladění. Samotný monitoring má minimální overhead, přitom je dle konfigurace defaultně vypnutý (latency-monitor-threshold 0) — doporučujeme jeho aktivaci (CONFIG SET latency-monitor-threshold 50). Výpis obsahuje záznamy od startu REDISu nebo od resetování — LATENCY RESET.

LATENCY DOCTOR

  • výpis přehledu připojení do REDISu. Užitečné pro detailní analýzu jednotlivých připojení. (pozn. níže uvedený příkaz nemá interaktivní mód, je tedy nutné si pomoci vlastním skriptem pro sběr dat v pravidelných a krátkých časových intervalech, ideálně v řádu malých jednotek sekund)

CLIENT LIST

  • zjištění struktury a velikosti uložených dat. Užitečné pro analýzu dat. (Upozornění: spuštění tohoto příkazu má mírný výkonnostní overhead, tedy nepouštět v největších výkonnostních zátěžích)

redis-cli --bigkeys

  • sledování paměti (od 4.x verze). Užitečné pro analýzu využití paměti, zda není někde nějaké úzké hrdlo v průběhu používání REDISu.

MEMORY DOCTOR

Pozn.: sledování latence je možné i přes redis-cli pomocí parametrů — latency nebo — latency-history. Toto sledování se provádí přes opakované volání PING requestů ve vysoké frekvenci, viz https://redis.io/topics/rediscli#monitoring-the-latency-of-redis-instances. Výhodou je interaktivní mód, který umožňuje sledování průběhu měření, nicméně pro samotnou analýzu reálného provozu je lepší používat interní monitorovací framework, který měří reálný provoz REDISu včetně reálných commandů.

Sentinel vs cluster

Používáme celkem častý model a to 1x master, 2x slave a to vše v jednom Sentinelu:

  • slave jsou pouze pro čtení a tedy slouží pouze jako backup řešení v případě failoveru
  • naše zkušenosti se Sentinelem jsou jednoznačně dobré — funguje to, je to stabilní
  • nemáme zapnutou persistenci, tedy data neukládáme, jsou držena pouze v paměti. Z pohledu aplikací se jedná o cache a samotná aplikace počítá s tím, že o data je možné přijít.
  • jedna REDIS instance v pohodě zvládne malé miliony klíčů a velikost paměti do 10 GB. Pokud se budou velikosti pohybovat v těchto mezích, pak bych volil jednodušší variantu se Sentinelem než Cluster, kde už je možné uvažovat o použití partitions a tím pracovat s mnohem většími objemy dat.
  • často je potřeba mít více logických instancí REDISu (např. cache pro různé aplikace nebo různá data pro různé business oblasti) — většinou je lepší použít oddělené instance REDISu z důvodu zcela oddělených konfigurací, nicméně REDIS samotný nabízí koncept databází, tedy možnosti mít jednu REDIS instanci a v ní několik zcela oddělených částí, které jsou zcela nezávislé, ale mají stejnou REDIS a Sentinel konfiguraci.

Aplikační doporučení

  • nepoužívat pouze základní datový typ klíč-řetězec, ale i ostatní datové typy, které jsou často rychlejší
  • time-to-live (TTL) — při vkládání dat je potřeba se zamyslet nad jejich platností, protože REDIS defaultně počítá s tím, že data budou uloženy na vždy
  • čím menší uložená data budou, tím rychlejší REDIS bude — do REDISu je možné ukládat velké objemy dat (stovky tisíc kB, MB, …), což samotnému REDISu nevadí, pokud jsou data jen uložena, ale je to úzké hrdlo v případě vstupně/výstupních operací, kdy je nutné, zvláště při větším počtu připojení, data přenést z nebo do daného připojení a to bere čas
  • pokud je k dispozici pro vybraný programovací jazyk knihovna, která podporuje connection poolling, tak určitě je k doporučení před verzí bez poollingu.
  • určitě využívat pipelining při volání více commandů nebo obecně preferovat multi-commandové verze jako např. MSET.
  • používání KEYS v aplikaci mě přijde jako anti-pattern, navíc z performance pohledu není zadarmo

Konfigurace REDISu a Sentinelu

  • velikost aktuálního output bufferu je ovlivněna zátěží (tj. počty zpracovávaných požadavků) a samozřejmě výstupem generovaným vstupními commandy. Velikost output bufferu lze omezovat, aby REDIS nemusel čekat, když klient nebude dostatečně rychle výstupní data odebírat. Více https://redis.io/topics/clients#output-buffers-limits
  • v případě, že REDIS “začne drhnout” resp. začne se zvyšovat latence odpovědí, pak by bylo vhodné zjistit zda ke swapování dochází a pokud ano, tak k jak velkému. Podrobný návod je zde: https://redis.io/topics/latency#latency-induced-by-swapping-operating-system-paging
  • obecně je vhodné si projít admin příručku a zkontrolovat nastavení infra pod REDISem, zvláště pokud je virtualizované. Doporučuji změřit si tzn. intrinsic latency, tedy latency samotného serveru, na kterém běží REDIS.
  • konfigurační parametr “diskless replication” — defaultní varianta má hodnotu “no”, nicméně z pohledu performance je lepší volba “yes”.
  • By default min-replicas-to-write is set to 0 (feature disabled) and min-replicas-max-lag is set to 10 — při zápisu dat do masteru a asynchronní replikaci do slave je vždy možné, že se rozpojí spojení mezi masterem a slavem a díky tomu slave začne mít zastaralá data. REDIS má mechanismus jak to kontrolovat, ale ten je standardně vypnutý
    https://redis.io/topics/sentinel#example-2-basic-setup-with-three-boxes
    pozn. toto je funkce samotného REDISu a pokud je vypnutá, tak se spolehá na failover proces Sentinelu
  • konfigurační parametr slave-priority 100 (default hodnota) — it is used by Redis Sentinel in order to select a replica to promote into a master if the master is no longer working correctly. Pomocí této hodnoty je možné Sentinelu napovědět jaký nový master má být vybrán, pokud puvodní přestane fungovat a tím ovlivňovat rozvržení masterů a slavů přes jednotlivé nody. Pokud se nastaví hodnota 0, pak daný slave nikdy nebude masterem. Do výběru nového mastera ještě vstupuje offset, tedy přednost dostane ten slave, který má z původního mastera více zreplikovaných dat, tj. má aktuálnější data. Více o procesu výběru nového mastera: https://redis.io/topics/sentinel#replica-selection-and-priority
  • Pokud začne být master nedostupný, tak se počká určitý čas (konf. parametr sentinel down-after-milliseconds), zda tomu tak skutečně bude. Poté se zvolí nový master ze zbývajících slaves (pouze za podmínky, že jsou příslušné Sentinel procesy propojené). Následně je timeout (konf. parametr sentinel failover-timeout) na vytvoření ustanovení nového mastera včetně přenastavení replikace na nový slave nebo nové slaves. Přenastavení se provádí postupně slave po slavu (určuje konf. parametr sentinel parallel-syncs). Tedy max. interval je down-after-milliseconds + failover-timeout, kdy nebude probíhat replikace dat z mastera na slavy a tedy je riziko, že o tyto data je možné přijít (za podmínky, že je vypnutá persistence, jako to mámě my).
  • při velkých objemech uložených dat a při velkém trafficu je nutné počítat s tím, že datový tok může být opravdu hodně velký (stovky mbit/s) — jak mezi masterem a aplikacemi, tak i mezi masterem a slavy z důvodu replikace dat. Nezapomínat, že i infrastruktura může být někdy omezující.
  • pokud budete chtít jakkoliv optimalizovat používání paměti, pak si určutě projděte tento přehled tipů: https://docs.redislabs.com/latest/ri/memory-optimizations/

Zátěžové testování

Zátěžové testy jsme nejdříve začali provádět pomocí nástroje redis-benchmark (https://redis.io/topics/benchmarks), ale nebyli jsme úplně spokojeni s možností nastavení samotných testů a nepodařilo se nám vytvořit dostatečně velkou zátěž na samotný REDIS. Mnohem více jsme byli spokojeni s nástrojem memtier_benchmark (https://github.com/RedisLabs/memtier_benchmark).

Pozn. bohužel dokumentace je k nástroji poskromnu a co se nám nepodařilo rozchodit je iniciální nalití dat pomocí funkce import. Existuje k tomu sice nějaký popis, sem tam nějaká informace na internetu (např. zde), ale pořád málo, aby nám to stačilo. Nakonec jsme úvodní load dat udělali přes “mass import” pomocí REDIS protokolu. Je to opravdu hodně rychlé, jen na MacOS je potřeba si dát pozor na konce řádků, protože v protokolu je vyžadováno \r\n.

Info z testování:

  • použitá topologie: 1x master, 2x slaves, Sentinel
  • testy jsme prováděli na počítači MacBook, 2,7 GHz Quad-Core Intel Core i7, 16 GB RAM, Flash disky
  • z pozorování vyplynulo, že není úplně důležité, kolik klientů máme připojených — 200 nebo 800 nebo 2000. Pořád to jede. Samozřejmě latence stoupá, ale z průměrných 60ms na 190ms pro 2000 klientů. To jsou nicméně pořád velice dobré odezvy.
  • co je naopak mnohem více důležité je, jak moc často se připojení znovu-používají (reconnecting), tedy zda se připojení drží nebo se pořád vytváří nové a nové. Např. pro 2000 klientů je 190ms latency, když se reconnect udělá jednou za 70 requestů; pokud se změní na každých 5 requestů, tak latence se hned zvedne na průměrných cca 600ms. Navíc klesne CPU někam k 60%.
  • jinak rečeno dokážu vytížit samotný REDIS na 100% CPU a více, ale pouze pokud se mu co nejméně mění connections, jinak vytíženost CPU klesá a zvyšuje se latence => overhead spojený s handlovaním připojení začíná být úzké hrdlo

Použitá parametrizace zátěžových testů:

memtier_benchmark -s redis1 -t 4 -c 500 --ratio=1:4 -n 2000 --reconnect-interval=70 --key-minimum=1 --key-maximum=100000 --key-pattern=G:G --data-size-range=100-6000 --data-size-pattern=S --hide-histogram

memtier_benchmark -s redis1 -t 4 -c 500 --ratio=1:4 -n 2000 --reconnect-interval=5 --key-minimum=1 --key-maximum=100000 --key-pattern=G:G --data-size-range=100-6000 --data-size-pattern=S --hide-histogram

Při změně, kdy pro každý request se bude vytvářet nové připojení, tak:

  • CPU klesne tak na 40–50%
  • průměrná latency se dostane přes 1000ms

memtier_benchmark -s redis1 -t 4 -c 500 --ratio=1:4 -n 2000 --reconnect-interval=1 --key-minimum=1 --key-maximum=100000 --key-pattern=G:G --data-size-range=100-6000 --data-size-pattern=S --hide-histogram

Pokud chcete vidět všechny nastavené parametry pro memtier, pak použijte--show-config.

V průběhu psaní tohoto článku se postupně připravujeme na upgrade na poslední 5.x verzi. Zatím provádíme takové “smoke” performance testy, kdy ty samé testy co jsme dělali pro 3.x verzi provádíme na stejném prostředí a ve stejných podmínkách i s verzí 5.x a tak nějak si tu novou verzi oťukáváme. A musím přiznat, že jsem čekal viditelné rozdíly v rychlosti resp. latency a nic takového se nekonalo. Těch vylepšení týkajících se performance a optimalizace práce s pamětí nebo I/O mezi verzemi 3.x a 5.x bylo hodně, ale zatím to nikde nevidím. Budeme zkoušet dál, přidávat složitější scénáře a hlavně pak i se samotnou aplikací zkoušet, tak jsem zvědavý …

--

--