Redis 8.10 umí uložit jména polí v hashi jen jednou. Samo se to nezapne
Redis vydal 29. července verzi 8.10.0. Hlavní novinkou je uložení, ve kterém klíče se stejnou sadou polí sdílejí jeden seznam jmen místo toho, aby si ho každý nesl zvlášť. Autor změny naměřil na 2 800 000 hashích úsporu kolem 49 %, samo se to ale nezapne: potřebuje buď nový příkaz HIMPORT, nebo nastavení, které je ve výchozím stavu vypnuté.
Redis vydal verzi 8.10.0 29. července jako ostrou, ne jako kandidáta. Oficiální obraz na Docker Hubu se značkou 8.10.0 je z následujícího dne. Poznámky k vydání jmenují proti řadě 8.8 šestnáct změn, ale první z nich je z jiného soudku než zbytek: nové vnitřní uložení hashů, které se jmenuje compact hash.
Hash je v Redisu tabulka dvojic jméno pole a hodnota, tedy to, do čeho se běžně ukládá jeden uživatel, jedna objednávka nebo jedno sezení. Když jich má aplikace miliony a všechny mají stejná pole, uloží se jméno každého pole tolikrát, kolik je klíčů. U pole email a dvou milionů uživatelů to je dva miliony kopií pěti písmen.

Jména polí se uloží do šablony a klíč si nese jen odkaz
Nové kódování zavádí šablonu: neměnný seznam jmen polí, který leží v paměti jednou a odkazuje se na něj každý klíč se stejnou sadou polí. Klíč si pak drží už jen své hodnoty. Redis šablony zakládá, sdílí a uvolňuje sám, aplikace o nich neví a všechny dosavadní příkazy nad hashem se chovají stejně jako dřív.
Jména polí jsou v šabloně seřazená (nejdřív podle délky, pak po bajtech), takže se index pole hledá půlením. Vedle jmen má šablona ještě malé pořadové číslo a klíč si nese právě to, ne osmibajtový ukazatel; komentář ve zdrojovém kódu t_hash.c uvádí, že odkaz na šablonu tak vyjde i na zhruba dva bajty. Kódování jsou dvě, template-listpack pro malé hashe a template-array pro ty, které se do listpacku nevejdou. Obě vypíše příkaz OBJECT ENCODING. Jeho vlastní stránka v dokumentaci je ale 31. července u hashů pořád neuvádí a končí u listpacku.
Nový příkaz HIMPORT posílá jména polí jednou za celou dávku
Druhá novinka je příkaz HIMPORT na hromadné plnění. Klient si nejdřív pojmenuje sadu polí a pak už posílá jen hodnoty:
HIMPORT PREPARE u name email age
HIMPORT SET user:1 u alice a@example.com 30
HIMPORT SET user:2 u bob b@example.com 25
Sada polí patří jedinému spojení. Ostatní klienti ji nevidí a zmizí, jakmile se spojení zavře nebo klient pošle RESET. Proti volání HSET pro každý klíč tím ubude provozu po síti i práce na straně serveru, a Redis si navíc z toho, že všechny klíče sdílejí jednu sadu polí, vezme pokyn uložit je rovnou v novém kódování.
Čísla jsou z jednoho běhu na jednom stroji
Kolik to ušetří, uvádí popis změny na jednom měření jejího autora. Sada měla 2 800 000 hashů nad stovkou různých sad polí, každý hash 20 až 50 polí (v průměru zhruba 34), jméno pole průměrně 13 bajtů a hodnota také 13 bajtů. Obyčejné hashe zabraly 3,49 GB, tytéž v novém kódování 1,77 GB, tedy asi o 49 % méně.
Je to vlastní běh autora změny, ne nezávislé měření, a jeho výsledek závisí na tom, jak velkou část klíče tvoří jména polí. Popis to říká rovnou: nejvíc ušetří klíče s krátkými hodnotami, protože u nich jsou opakovaná jména polí většina objemu. U klíče s jednou dlouhou hodnotou je úspora malá.
Co za to
Šablona je neměnná. Když se do hashe přidá nové pole nebo se jedno smaže, šablona se neupraví – klíč se přesune k jiné, kterou Redis buď najde v registru, nebo pro něj vyrobí. Provoz, který jména polí často mění, tak za úsporu paměti platí hledáním a zakládáním šablon při každém takovém zápisu. Čtení a přepis hodnoty existujícího pole zůstávají stejně rychlé jako dřív.
Změna je navíc jednosměrná: jakmile klíč přejde na šablonu, zůstane u ní po celý svůj život. Mezi šablonami se stěhuje podle toho, jak se mění sada polí, ale zpátky na obyčejný hash se nevrátí. Jedinou výjimkou je vypršení jednotlivých polí – HEXPIRE nad takovým klíčem ho převede zpět na obyčejný hash a úspora je pryč. Hashe, které vypršení polí používají, se do šablony nepřevádějí vůbec.
Poslední rozdíl se týká procházení. HSCAN vrátí u klíče v novém kódování všechna pole naráz a kurzor rovnou nulový. Klientovi, který cyklí do nuly, to nevadí, ale u velmi širokého hashe dorazí v jedné odpovědi to, co by se dřív rozpadlo do několika dávek.
Samo se to nezapne
Automatický převod je vypnutý. Řídí ho pětice nových nastavení v redis.conf a všechna mají výchozí hodnotu 0, což u nich znamená „nedělej nic“. Dvě platí pro běžné zápisy: hash-min-template-entries říká, od kolika polí se hash při dalším zápisu převede, hash-max-template-entries pak horní mez, aby se hodně široké hashe do sdíleného registru nedostaly. Změna se projeví líně, tedy až při dalším zápisu do konkrétního klíče, ne v okamžiku, kdy ji správce nastaví.
Zbylá tři nastavení převádějí hashe při načítání RDB souboru a míří na jednorázový přechod ze starších verzí: soubor uložený dřív, než šablony vznikly, se dá při načtení převést, aniž se data přepisují. Na soubor, ve kterém už nějaká šablona je, se neuplatní. K nim patří práh hash-rdb-load-template-disassembly-threshold: šablona, kterou na konci načítání sdílí míň klíčů, než kolik práh žádá, se rozebere zpět na obyčejné hashe, protože by víc paměti spotřebovala, než ušetřila. Týž práh slouží i jako pojistka – jakmile vznikne aspoň tisíc šablon a víc než polovina z nich je pod prahem, Redis uprostřed načítání nové šablony zakládat přestane a zbytek dat nechá tak, jak je.
Kolik toho šablony zabírají, hlásí INFO
Kolik šablon server drží a kolik klíčů na nich visí, hlásí INFO stats v položkách hash_templates a hash_template_keys. Kolik paměti šablony zabírají dohromady, uvádí INFO memory jako used_memory_hash_templates. Příkaz MEMORY USAGE k paměti jednoho klíče připočte jeho podíl na sdílené šabloně, rozpočítaný rovným dílem mezi všechny klíče, které ji používají.
Ostatní body seznamu se týkají jednotlivých příkazů. LMOVEM a BLMOVEM přesunou mezi seznamy víc prvků najednou, SUNIONCARD a SDIFFCARD vrátí počet prvků sjednocení a rozdílu množin, aniž je posílají, XREAD a XREADGROUP umí novými parametry MAXCOUNT a MAXSIZE omezit velikost odpovědi, přibyl příkaz BACKUP pro zálohu nad vícedílným AOF a servery mezi sebou se nově dokážou ověřit certifikátem protistrany v TLS. Sedm zbylých bodů patří vyhledávacímu a časovému modulu: výpis aliasů indexu, stemmery pro malajštinu a tagalog, rozšíření JSONPath a čtyři změny nad časovými řadami. Poslední položkou seznamu je obecné „zrychlení“ bez podrobností.
Zdroje: poznámky k vydání 8.10.0, popis a rozprava k změně 15364, konfigurační soubor ve značce 8.10.0, dokumentace příkazu HIMPORT a oficiální obraz na Docker Hubu.