- Praktický úvod do Redis (1): vaše distribuovaná NoSQL cache
- Praktický úvod do Redis (2): transakce
- Praktický úvod do Redis (3): cluster
Redis je úžasně jednoduché a při tom velmi mocné NoSQL in-memory key-value úložiště. Potřebujete akcelerovat vaší aplikaci jednoduše a při tom využít distribuované zdroje a současně mít redundanci nebo transakční ochranu? Tohle je úkol pro Redis.
Redis – NoSQL in-memory kouzelník
Pokud vaše aplikace musí čekat na data přicházející z disků, SAN, ale i flash, bude to z pohledu rychlostí procesoru trvat celou věčnost. Jasně – vaše storage pole má flash cache, databázový engine drží poslední přistupovanou stránku v paměti nebo máte v serveru nějaký I/O akcelerátor. Pokud ale máte možnost ovlivnit aplikaci, bude to lepší, než zmíněné postupy probíhající na nízké úrovni, která netuší co vlastně aplikace dělá. Implementujte inteligentní cache v paměti – Redis.
Tento systém tedy asi nebudete používat jako primární datovou vrstvu, ta bude třeba v nějaké klasické SQL či jiné NoSQL databázi. Pokud vaše aplikace potřebuje data podle nějakého klíče, měla by nejprve zavolat Redis. Pokud něco takového ještě žádáno nebylo, Redis nevrátí nic. Aplikace se zeptá primární databáze, ale odpověď uloží i do Redis, takže příště už bude dotaz obsloužen z paměti. V rámci microservices designu je pochopitelně ideální tuto logiku oddělit od aplikace – takže naimplementujte třeba RESTful službu, která vrací údaje, takže aplikační logiku nemusí zajímat, jak se k datům dopracovala (a služba použije kombinaci Redis a dotazu do databáze je-li třeba). Podobně může mít služba zapisovací REST operace (nechci to teď komplikovat, ale zapisování je lépe provádět přes redundantní message bus typu RabbitMQ s několika subscriber / worker node … ale o tom někdy jindy), které nepíší jen do Redis (protože tam není 100% garance trvalého zápisu), ale mohou zapsat do primární databáze a současně do Redis, pokud očekávají, že na data budou hned vznikat nějaké dotazy a dává smysl je v cache mít (všimněte si výhody aplikační implementace v porovnání s low-level pojetím – můžete vymýšlet i dost složitou logiku co v cache má být a jak to má expirovat).
Oproti běžné cache ale umí Redis pár zajímavostí. Podporuje sharding, takže spojíte několik uzlů dohromady a vznikne jedno společné úložiště (dejte dohromady pár přebývajících pamětí). Umožňuje master-slave replikaci, takže i při výpadku nodu nemusíte všechna jeho data načítat znova z databáze. Navíc se s ním dá pracovat i transakčním způsobem.
Dnes se zaměříme na instalaci a základní ovládání.
Instalace
Přestože existují v distribucích Linux hotové balíčky, instalace ze zdroje je jednoduchá a velmi rychlá. Takhle jsem ji provedl v Ubuntu 14.04:
wget http://download.redis.io/redis-stable.tar.gz tar xvzf redis-stable.tar.gz cd redis-stable make sudo make install
V produkčním prostředí používejte Redis jako systémovou službu dle návodu na oficiálních stránkách. Nám pro učení bude stačit spustit ho jako běžnou aplikaci:
cloudsvet@ubuntu:~$ redis-server & [1] 4448 hp@ubuntu:~$ 4448:C 26 Aug 08:08:16.773 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf 4448:M 26 Aug 08:08:16.774 # You requested maxclients of 10000 requiring at least 10032 max file descriptors. 4448:M 26 Aug 08:08:16.774 # Redis can't set maximum open files to 10032 because of OS error: Operation not permitted. 4448:M 26 Aug 08:08:16.774 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'. _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 3.0.3 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 4448 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 4448:M 26 Aug 08:08:16.774 # Server started, Redis version 3.0.3 4448:M 26 Aug 08:08:16.774 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. 4448:M 26 Aug 08:08:16.774 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled. 4448:M 26 Aug 08:08:16.774 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 4448:M 26 Aug 08:08:16.774 * The server is now ready to accept connections on port 6379 [/bash/ Redis má příkazovou řádku - spustíme ji a ověříme spojení. cloudsvet@ubuntu:~$ redis-cli 127.0.0.1:6379> ping PONG
Pracujeme s daty
Vyzkoušejme si teď pár velmi jednoduchých operací. Redis je Key-value store, takže pod nějakým klíčem ukládá hodnotu – velmi jednoduché. Takhle můžete vytvořit a přečíst záznam:
127.0.0.1:6379> set tomas "Pod stromem 55, Praha 5" OK 127.0.0.1:6379> set martin "Nad kopcem 18, Benesov" OK 127.0.0.1:6379> get tomas "Pod stromem 55, Praha 5" 127.0.0.1:6379> get martin "Nad kopcem 18, Benesov"
Můžeme jeden z nich třeba smazat a uvidíme, že dotaz vrátí prázdnou hodnotu:
127.0.0.1:6379> del martin (integer) 1 127.0.0.1:6379> get martin (nil)
Pokud chcete hodnotu přiřadit jen, pokud klíč dosud neexistuje, použijte setnx:
127.0.0.1:6379> setnx klic hodnota1 (integer) 1 127.0.0.1:6379> setnx klic hodnota2 (integer) 0 127.0.0.1:6379> get klic "hodnota1"
Založte pár dalších záznamů. Abyste šetřili čas (přeci jen jde o cache, takže rychlost je důležitá), můžete si vyžádat hodnotu vícero klíčů s použitím mget (podobně funguje mset pro vytvoření několika key-value v jednom volání). Klíč martin jsem obnovil, ale marenka neexistuje:
127.0.0.1:6379> mget tomas martin marenka 1) "Pod stromem 55, Praha 5" 2) "Nad kopcem 18, Benesov" 3) (nil) Můžete si vyžádat jen část hodnoty, třeba prvních pět znaků: 127.0.0.1:6379> getrange tomas 0 5 "Pod st"
Protože nejčastější implementací Redis je cache, data nám mohou zastarávat. V nějakém okamžiku budeme chtít, aby se smazala. Redis umožňuje u každého klíče nastavit platnost jeho trvání (pro každý klíč zvlášť – vaše logika tak může podle typu dat nebo kontextu nastavit expiraci tak akorát, což je velký rozdíl oproti low-level přístupu, kdy nevíte co ta data znamenají). Příkazem expire nastavíte samodestrukční budík na klíči, příkaz ttl ukazuje, kolik mu zbývá:
127.0.0.1:6379> set marek "Prechodna 25, Praha" OK 127.0.0.1:6379> expire marek 10 (integer) 1 127.0.0.1:6379> get marek "Prechodna 25, Praha" 127.0.0.1:6379> ttl marek (integer) 6 127.0.0.1:6379> ttl marek (integer) 3 127.0.0.1:6379> ttl marek (integer) -2 127.0.0.1:6379> get marek (nil)
Mimochodem použili jsme verzi příkazu s vteřinovou přesností, ale můžete využít milisekundovou variantu pexpire.
Pokud chcete nastavit expirace rovnou při vytvoření klíče, použijte setex – ušetříte jedno volání:
127.0.0.1:6379> setex zmizi 10 to OK 127.0.0.1:6379> ttl zmizi (integer) 8
Redis je klasický key-value store, takže vyhledávat podle value nemůžete. Nicméně je tu možnost vyhledávání a vypisování klíčů. Přidal jsem pár dalších záznamů a takto si mohu vypsat, jaké klíče Redis drží:
127.0.0.1:6379> keys * 1) "tomas" 2) "maruska" 3) "oskar" 4) "martin" 127.0.0.1:6379> keys mar* 1) "maruska" 2) "martin"
Zejména první příkaz zase raději rychle zapomeneme – v Redis můžete mít miliardy klíčů a tohle by nebyl dobrý nápad. V produkci je bezpečnější použít příkaz scan, který provádí stránkování výsledků – zadáte mu na začátek index 0 a on vám pošle nějakou várku klíču a také další číslo indexu, odkud máte pokračovat (pokud je vrácený index 0, jste na konci a máte hotovo).
127.0.0.1:6379> scan 0 1) "0" 2) 1) "tomas" 2) "maruska" 3) "oskar" 4) "martin" 127.0.0.1:6379> scan 0 match mar* 1) "0" 2) 1) "maruska" 2) "martin"
Hodnota může být ve skutečnosti seznam. Podle toho, jestli chceme přidávat na konec nebo na začátek, použijeme rpush nebo lpush. Vypsat můžeme příkazem lrange:
127.0.0.1:6379> rpush seznam Okurky (integer) 1 127.0.0.1:6379> rpush seznam Chleba (integer) 2 127.0.0.1:6379> rpush seznam Mouka (integer) 3 127.0.0.1:6379> lrange seznam 0 2 1) "Okurky" 2) "Chleba" 3) "Mouka"
Samozřejmě existují operace jako vložení doprostřed nebo vymazání položky v seznamu.
Přístup z aplikace
Pokud používáte například Python, ukažme si, jak lze aplikačně k Redis přistupovat (knihovny existují pro většinu běžných programovacích jazyků).
Pro stažení redis knihovny použijte třeba pip:
sudo pip install redis
Takhle vypadá jednoduchý program, který uloží hodnotu, přečte a zase smaže:
import redis mujredis = redis.Redis('127.0.0.1') mujredis.set('mujklic', 'mojehodnota') print "Hodnota mujklic je " + mujredis.get('mujklic') mujredis.delete('mujklic')
Po spuštění to dělá tohle:
cloudsvet@ubuntu:~$ python redisdemo.py Hodnota mujklic je mojehodnota
A co příště?
Dnes jsme se zaměřili na instalaci, základní operace a přístup z aplikace. Příště si ukážeme transakčně bezpečné operace, sharding (rozprostředí přes víc nodů) a replikaci (master-slave). A to bude všechno – Redis opravdu není nic složitého.