Redis a jeho použití v Pythonu

Co je to Redis?

Redis je nerelační, no-sql databáze typu key-value. To znamená, že data jsou v něm uložena pod nějakým (nejčastěji stringovým) klíčem a abychom si je mohli načíst nebo je zapsat, tento klíč potřebujeme znát, respektive ho vyrobit.

clanek_123 -> {"title": "Nadpis 1", "content": "Ahoj světe!"}
clanek_135 -> {"title": "Nějaký jiný nadpis", "content": "Obsah článku"}

Na rozdíl třeba od MySQL databáze, uložená data nemají schéma, což znamená, že zodpovědnost za to, jak data "vypadají" neleží na straně databáze, ale aplikace. Pokud v MySQL databázi ukládáme článek, ukládáme jej do tabulky, která má přesně definované sloupečky, které reprezentují atributy článku. Redisu je jedno, co pod nějaký klíč uložíte. Oproti relačním databázím neuděláte v Redisu jednoduše vazby mezi entitami. Pokud budete mít entitu článek a ten bude mít autora, můžete entitu autora uložit jako zanořenou strukturu entity článek, případně se ve článku odkázat na jiný redis klíč, ve kterém bude autor uložen a který si načteme druhým dotazem.

clanek_333 -> {"title": "Nadpis", "content": "Obsah"}
clanek_444 -> {"title": "Jiný nadpis", "content": "Tralala", "author": {"first_name": "Karel", "last_name": "Kovář"}}

💡 Databázi pro svůj projekt volte vždy podle funkčních požadavků a podoby dat. Pokud provedete analýzu a mezi entitami najdete spoustu relací, není vhodné použít no-sql databázi jako primární uložiště dat.

Základní vlastnosti Redisu

  • Bleskově rychlý díky tomu, že data jsou uložena primárně v operační paměti.
  • K datům se přistupuje pomocí klíčů, bez dotazovacího jazyku.
  • Podpora v mnoha jazycích (Python, C++, Java, Go, JavaScript, ...)
  • Dobře škálovatelný do šířky (přidáváme celé stroje) i do hloubky (přidáváme paměť a procesor).
  • Open source

K čemu se Redis hodí?

  • cachování. Představte si, že pro svůj e-shop používáte jako hlavní uložiště dat MySQL databázi. V databázi je uložené zboží s košatou datovou strukturou. Každá položka má své vlastnosti (výšku, váhu), výrobce, dostupnost ve skladu apod. Data se budou častěji číst, než zapisovat, tudíž se nebudou zase tak často měnit. Vhodné je tedy položku načíst z databáze včetně všech jejich vazeb a v jednoduchém slovníku uložit do Redis databáze pod klíčem zbozi_XY. Když pak návštěvník přijde na náš e-shop a otevře si url /zbozi/XY, aplikace se nejdříve podívá do Redisu, jestli tam nejsou uložena data a pokud jsou, načte si je. Pokud nejsou, načte je z MySQLky.
  • uložiště "proměnných", sessions. Pokud potřebujeme uložit jednoduchou proměnnou a mít k ní přístup z vícero aplikací nebo instancí aplikace, dává smysl ji uložit do Redisu. Díky své rychlosti je Redis vhodné uložiště pro ukládání uživatelských sessions (tajemství, které je uloženo u uživatele například v cookie a které se posílá na server, kde pak zjistíme jestli je session platná a jakému uživateli patří).
  • fronty, odběry. Jedna z datových struktur, se kterou Redis dokáže pracovat, je seznam, nad kterým nabízí několik operací/příkazů. Ze seznamu dokážeme udělat velice rychlý zásobník nebo frontu. Pokud pracujete se streamy nebo na realtime aplikacích, oceníte možnost přihlásit se k odběru zpráv a využít tak Redis jako alternativu ke Kafce.

Instalace Redisu a klient redis-cli

Redis nainstalujete bez problémů do všech unixových systémů, pro produkční používání však doporučuji Linux. Pro účely dalšího povídání se však nebudeme zdržovat samotnou instalací a použijeme docker kontejner s předinstalovaným Redisem.

docker run -p 6379:6379 redis

Výchozí redis port 6379 zpropagujeme z kontejneru na hostující stroj a zkusíme se k němu připojit skrze nástroj redis-cli. V debian-like systémech ho najdete v balíčku redis-tools, v OSX se dá nainstalovat přes brew).

redis-cli -h localhost

Otevřeme interaktivní konzoli. Parametr -h je v našem případě zbytečný, protože pouhé spuštění redis-cli se připojí na výchozího hosta (localhost) a port (6379), což je přesně to, co jsme z dockeru vypropagovali ven.

Základní příkazy

127.0.0.1:6379> SET article_counter_123 1
OK
127.0.0.1:6379> GET article_counter_123
"1"
127.0.0.1:6379> INCR article_counter_123
(integer) 2
127.0.0.1:6379> DEL article_counter_123
(integer) 1

Zkusíme nasimulovat uložiště pro jednoduché počítadlo přečtení článku. Prvním příkazem (SET) uložíme do databáze první záznam. Uložíme ho pod klíčem article_counter_123. Druhým příkazem (GET) si data vyčteme zpět. Abychom nemuseli počítadlo inkrementovat na straně aplikace, což by znamenalo data si z databáze vyčíst, inkrementovat a znovu zapsat, využijeme příkaz INCR. Příkazem DEL záznam smažeme.

127.0.0.1:6379> SET article_123 "{\"title\": \"Nadpis\"}" EX 360
OK
127.0.0.1:6379> SET article_555 "{\"title\": \"Nadpis 2\"}" EX 360
OK
127.0.0.1:6379> KEYS *
1) "article_123"
2) "article_555"
127.0.0.1:6379> KEYS article*
1) "article_123"
2) "article_555"

Každému záznamu můžeme nastavit expiraci v sekundách. Pokud má záznam nastavenou expiraci, automaticky se z databáze po stanovené době smaže, což se nám hodí, když Redis používáme pro cachování. Příkazem KEYS [filtr] si můžeme vyfiltrovat seznam existujících klíčů v databázi podle zadaného prefixu. Pro filtrování můžeme použít wildcard * reprezentující libovolný suffix.

127.0.0.1:6379> RPUSH seznam prvni
(integer) 1
127.0.0.1:6379> RPUSH seznam druhy
(integer) 2
127.0.0.1:6379> RPUSH seznam treti
(integer) 3
127.0.0.1:6379> LRANGE seznam 0 -1
1) "prvni"
2) "druhy"
3) "treti"

Redis dokáže velice pěkně pracovat se strukturou seznamu. Díky příkazům RPUSH a LPUSH dokážeme do seznamu přidávat další položky buď na jeho začátek nebo konec. Pomocí příkazů LPOP a RPOP zase dokážeme data ze seznamu vyčíst a pracovat s ním jako s frontou nebo zásobníkem. Příkazem LRANGE si vyčteme všechny položky seznamu mezi dvěma indexy.

127.0.0.1:6379> SET klic1 hodnota
OK
127.0.0.1:6379> SET klic2 hodnota2
OK
127.0.0.1:6379> MGET klic1 klic2
1) "hodnota"
2) "hodnota2"

Výborná věc jsou příkazy MGET nebo MSET, pomocí kterých můžete vyčíst či zapsat více záznamů současně. Když v aplikaci dopředu víte, jaké klíče si potřebujete načíst, nemusíte ztrácet čas komunikací po síti pro každý klíč zvlášť, ale jedním dotazem si získat všechny potřebné hodnoty.

🔗 Seznam všech dostupných příkazů včetně podrobné dokumentace a příkladů můžete najít zde: https://redis.io/commands/

Připojení k Redisu pomocí Pythonu

import redis
import sys

ARTICLE_COUNTER_KEY = "article_counter_{article_id}"
redis_client = redis.Redis(host="localhost", port=6379, db=0)

def get_counter_and_increment(article_id):
    counter = redis_client.incr(ARTICLE_COUNTER_KEY.format(article_id=article_id))
    return counter

if __name__ == "__main__":
    if len(sys.argv) > 1:
        article_id = sys.argv[1]
        counter = get_counter_and_increment(article_id)
        print(f"Clanek {article_id} byl nacten {counter}x")

Skript vytvoří jednoduchého klienta (instance redis.Redis), pomocí kterého můžete rovnou spouštět libovolné redis příkazy. Ve funkci get_counter_and_increment zavoláme příkaz INCR, který nám povýší konkrétní počítadlo a jeho novou hodnotu vrátí z funkce ven. Když si pak zkusíme skript spustit s různými argumenty, dostaneme počet "přečtení", respektive, v našem případě počet spuštění skriptu s konkrétním argumentem.

$ python redis-counter.py 100
Clanek 100 byl nacten 1x
$ python redis-counter.py 100
Clanek 100 byl nacten 2x
$ python redis-counter.py 100
Clanek 100 byl nacten 3x
$ python redis-counter.py 101
Clanek 101 byl nacten 1x
$ python redis-counter.py 102
Clanek 102 byl nacten 1x

 

Štítky
Úroveň znalostí
Začátečník