Redis – moć brzine i jednostavnosti

Šta je REDIS?

Za Redis (REmote DIctionary Server) se uopšteno može reći da je napredna key value baza koja se u poslednje vreme sve više koristi. Budući da kod nas nema  tekstova na ovu temu želja mi je da kolegama programerima predstavim ovaj zaista moćan alat. O njegovoj rastućoj popularnosti dovoljno govori to da se Redis gotovo uvek koristi kao jedno od rešenja kada se radi reinženjering sajtova sa velikom posetama. Danas ga srećemo u aplikacijama kao što je Instagram, Pinterest, Youporn, Tumblr, StackOverflow, Disqus, Guardian, Github, Blizzard i mnogi mnogi drugi. Potpuno je besplatan,  napisan u jeziku ANSI C u svega dvadesetak hiljada redova. Napisao ga je italijan Salvatore Sanfilippo (@antirez). Od 2010 godine Redis sponzoriše ga VMVare alli je i dalje ostao besplatan. Podržava master-slave replikaciju a ono čemu je autor posvetio punu pažnju u novoj verziji je clustering i podrška izvršenju Lua skripti nad Redis serverom.

Glavne prednosti Redisa:

  • Brzina izvršenja (najbrži key value store),
  • Izutno je lagan za učenje (jednostavna konfiguracija i pokretanje)
  • Aktivan razvoj (svakodnevno se razvija i nadogradjuje)
  • Omogućava master-slave replikaciju što doprinosi skalabilnosti
  • Atomske operacije

REDIS – server struktura podataka

Redis ne treba shvatiti kao običan key value skladište podataka kao što je Memcache. Redis jeste to ali je i mnogo, mnogo više – to je zapravo server struktura podataka. Struktura podataka je način na koji se podatak zapisuje u računar. Razumevajući strukture u Redisu, kako rade, koje komande su nad njima moguće i koje podatke možemo čuvati u njima ključno je za razumevanje samog Redisa. Redis je server sledećih struktura podataka:

  1. Stringovi (Strings ) – predstavljaju osnovnu strukturu key value pristupa. Ključevi mogu sadržati bilo šta (budući da su binary safe) od podataka veličine do 512 MB. Dakle ključ ili vrednost može može da bude npr. sadržaj nekog JPG fajla, json, običan tekst itd…
  2. Skupovi (Sets) – predstavljaju neuredjenu kolekciju jedinstvenih elemenata. Dakle Redis ne dozvoljava ponavljanje elemenata u skupovima što znači da programeri ne moraju da vode računa o tome da li neki element već postoji u skupu ili ne – Redis to odradjuje umesto njih. Programeri pozivaju osnovne komande za dodavanje, izbacivanje, presek, uniju i razliku elemenata u skupovima. Maksimalan broj elemenata je 232 -1 tj. ukupno 4294967295 elemenata po skupu. Skupovi predstavljaju idealnu strukturu za društvene mreže upravo zbog osnovnih komandi nad elementima.
  3. Sortirani skupovi (Sorted sets) – predstavljaju uredjenu kolekciju jedinstvenih elemenata. Da bi postigla uređenost koristi se rezultat (score) tj. svakom elementu se priključuje kao podatak rezultat na osnovu kojeg se rangiraju elementi. Budući da su elementi jedinstveni svaki element će imati jedan i samo jedan rezultat. Maksimalan broj elemenata je 232 -1 tj. ukupno 4294967295 elemenata po skupu. Podržane su komande nad skupovima kao i kod običnih skupova i uz to, omogućene su komande pomoću kojih lako dolazimo do podataka kao što su prvih deset elemenata po rezultatu, rang elementa na osnovu rezultata, elementi koji imaju rezultat između određenih vrednosti itd.
  4. Heševi (Hashes) – predstavljaju zapravo asocijativni niz. Ruby programerima je dobro poznat ovaj termin budući da su već sreli sa pojmom heševa u svom jeziku. Dakle heševi barataju sa nizovima stringova tj postoji više parova ključ-vrednost u okviru heša. Heš se koristi kada nam je potrebna struktura slična tabelama u bazama podataka. Potrebno je naglasiti da heševi ne zauzimaju puno mesta tako da se milioni objekata mogu sačuvati na relativno malom prostoru.
  5. Liste (Lists) – predstavljaju listu stringova. Elementi su sortirani po redosledu ubacivanja u listu. U suštini liste se mogu posmatrati kao nizovi. Elementi se mogu dodavati na početak (glavu) i na kraj (rep) liste. Maksimalan broj elemenata je 232 -1 tj. ukupno 4294967295 elemenata po listi. Elementi se ubacuju u listu u konstantnom vremenu tj isto je vremena potrebno kada ubacujemo element u listu od 10 i u listu od 10 miliona elemenata. Liste vrlo brzo vraćaju rezultate kao što su prvih n elemenata sa glave, tj sa početka dok su malo sporije kada tražimo elemente u sredini liste.

Redis komande vs SQL upiti

Za svaku od navedenih struktura Redis ima posebne komande. Listu svih komandi možete pogledati ovde. Ukoliko želite da probate Redis to možete učiniti na ovoj lokaciji sa napomenom da na ovoj verziji ne možete koristiti slova kao što su šđlčć niti ćirilična slova. Napominjem da je ovo moguće na verziji Redisa koju sam ja instalirao ali ovde je verovatno u pitanju neka starija verzija.

Možemo slobodno reći da su programerima bliže komande nego upiti. U svakom od programskih jezika mi baratamo sa nekakvim komandama koje primaju odredjeni broj parametara i dobijamo nekakav rezultat. Upravo takav slučaj je i u Redisu koji se može smatrati produženom rukom našeg programskog jezika gde nam je za dobijanje željenih podataka potreban naziv ključa i parametri bez obzira o kojoj strukturi podataka se radi. Upiti nad klasičnim SQL bazama mogu da budu manje i više složeni i programeri u suštini dobro barataju i sa upitima. Međutim kada imamo veliku količinu podataka a informacije su nam brzo potrebne dolazimo u problem. Potrebno je optimizovati bazu, postaviti indekse itd… a to i nije baš teritorija gde se programeri dobro snalaze. Štaviše većina tvrdi da bez obzira na godine iskustva čini im se da to nikad nije na nivou na kom bi oni očekivali.

Pored standardnih komandi Redis barata i sa transakcijama. Transakcije u Redisu predstavljaju niz komandi koje čekaju u redu dok se ne pozove njihovo izvršenje. Kada se pozove izvršenje,vreme obrade je višestruko brže nego da smo komande puštali odvojeno. Takođe je nemoguće da se komanda nekog drugog klijenta poremeti izvršenje što znači da su Redis transakcije atomske operacije. Redis transakcija se razlikuje od klasične RDBMS transakcije jer ne postoji rollback. Razlog tome je što greška u Redis komandama može doći ili usled pogrešne sintakse ili se pogrešno pozove odredjena komanda nad pogrešnim tipom podataka pa se očekuje da se ovakve greške uoče i otklone u razvojnoj a ne fazi produkcije. Drugi razlog je vezan za brzinu – Redis je razvijan i uprošćen da bude brz a ne da omogućava transakcije. To u prevodu znači da se ne možemo osloniti na Redis kada su u pitanju operacije koje zahtevaju sigurno izvršenje (npr. bankarske transakcije) a u praksi se Redis i ne koristi za ovakve stvari. Najčešće se kombinuje sa drugim rešenjima koja omogućavaju sigurnost izvršenja operacija.

Organizacija podataka u Redisu

Kada analiziramo neki problem najpre pristupimo analizi podataka i izvršimo organizaciju podataka. Budući da u velikoj većini slučajeva koristimo neki RDBMS naši podaci su organizovani u skladu sa principima relacionih baza podataka. Relacioni model je vodjen strukturom dostupnih podataka tj. glavno pitanje je –  “Koje odgovore ja imam?”. To u praksi znači da smo mi organizovali podatke i onda u aplikaciji pravimo upite na osnovu dostupnih podataka.

U Redisu, kao i u svakom drugom NOSQL rešenju, pristup je potpuno drugačiji. Osnovno pitanje koje se ovde postavlja jeste – “Koja pitanja ja imam?” . To u praksi znači da ćemo podatke organizovati tako da što lakše i brže dodjemo do njih. U Redisu je sasvim normalna pojava da se isti podaci čuvaju na više mesta kako bi ispunili glavni cilj a to je brza dostupnost podataka. Budući da u Redisu ne postoji nikakva vrsta referencijalnog integriteta to morate napraviti na nivou same aplikacije. Ovo je cena za neuporedivo brži pristup podacima u Redisu (detaljnije o performansama u sledećem blogu) ali uz dobru organizaciju ni ovo nije neki veliki problem.

Najvažnija stvar u Redisu jeste odabrati adekvatan naziv i izvršiti dobru organizaciju ključeva. Stoga je potrebno napraviti takve modele na aplikativnom nivou koji će sami generisati nazive ključeva kako bi mogli da podatke grupišemo logičku celinu i bez problema izvršavamo CRUD operacije. Ovo je naročito bitno priliko brisanja podataka i očuvanja referencijalnog integriteta jer se podaci, kao što smo rekli, pojavljuju na više mesta. Svaki model bi se sastojao od više polja a svako polje bi predstavljalo jednu strukuru podataka.

U praksi bi to značilo da ako npr. imamo korisnika čiji je ID 1 tada ćemo njemo ključeve za njegove podatke čuvati u obliku user:1:username (String struktura),  user:1:personal_data (Hash struktura sa dodatnim poljima kao što su name, surname, age…). Ukoliko korisnik može da sklapa prijateljstvo sa drugim korisnicima onda bi imali polje user:1:friends_list (Set struktura). Za korisnika sa ID 2 ovaj ključ bi bio user:2:friends list. Prvi set – user:1:friends_list sastoji se od jednog člana, tj tu se nalazi ID korisnika 2 dok se kod drugog korisnika Set user:2:friends list sastoji od člana 1. Kada bismo brisali člana sa ID 2 morali bismo pored njegovih podataka u modelu napraviti takvu logiku koja će ga izbrisati i iz seta user:1:friends_list. Kao što sam rekao uz dobru organizaciju modela ovakvo nešto nije komplikovano i rešava se samo jednom metodom u modelu.

Postojanost podataka

Redis podržava dva načina čuvanja podataka – podrazumevana RDB (binarno) i AOF (append only file). U zavisnosti od konkretne upotrebe biramo koji način nam više odgovara. I jedan i drugi imaju svoje prednosti i mane.

U RDB-u podaci se čuvaju u memoriji i periodično se snimaju na disk. Koristeći RDB, u slučaju nekakve havarije može doći do gubitka dela podataka. Ukoliko nam Redis server služi kao cache ili session handler ovo predstavlja idealan izbor.

U AOF-u biramo kako će se naši podaci skladištiti iz memorije u disk – svake sekunde, posle svakog upita ili nikad. Pored podataka o ključevima i vrednostima AOF čuva i sve zapise o komandama koje su pokretane nad serverom i to u samo jednom fajlu, dakle podaci, ključevi i logovi zajedno. Ovo ima jednu veliku prednost jer ukoliko imamo dva servera i žellimo da spojimo njihove podatke u treći server dovoljno je da sastavimo njihova dva fajla u kojima se čuvaju ključevi, podaci i logovi i novi server je spreman.

Logično je da će Redis znatno brže baratati sa podacima iz memorije nego da svaki put upisuje na disk. Detaljnije o podešavanjima perzistentnosti Redis servera možete pogledati na zvaničnom sajtu.

Gde i kada koristiti Redis

Redis se može kombinovati kako sa NOSQL tako i sa SQL rešenjima. Najčešće se koristi kao session handler i kao napredni alat za keširanje budući da se njegovim strukturama i operacijama koje možete izvršiti nad njima predstavlja znatno moćnije rešenje od memcache-a.Treba ga upotrebiti u situacijama kada se može iskoristiti prednost njegovih struktura pa ćemo i primere dati po strukturama.

  1. Stringovi se najčešće koriste kao brojači ( korisne su komande INCR, INCRBY, DECRBY). Budući da Redis podržava atomske operacije, ukolilko dva klijenta konkurentno pristupaju istom ključu Redis će znati da se izbori sa tim te će uspešno odgovoriti na zahtev i jednom i drugom.
  2. Setovi se koriste kada treba čuvati skupove u kojima je potrebno da članovi budu jedinstveni. Uzmimo za primer društvenu mrežu sa velikim brojem korisnika. Pratimo ko je trenutno ulogovan i listu prijatelja korisnika. Dakle korisniku treba u deliću sekunde ko je od svih trenutno ulogovanih korisnika njegov prijatelj ili npr ko su mu zajednički prijatelji sa nekim drugim korisnikom. U ovakvim situacijama do izražaja dolazi Set struktura i operacija preseka (intersection). Rezultat se dobija u deliću sekunde bez obzira na broj članova dok bi u SQL rešenjima operacija spajanja znatno duže trajala. I operacije unije i preseka skupova takođe ovde dolaze do izražaja. Pri upisu korisnika u skup trenutno ulogovanih korisnika ne moramo da vodimo da li se on već tu nalazi ili ne, mi ga samo upisujemo a Redis ga neće dodati u taj skup ukoliko je već tu. Set je koristan i kada se vrši praćenje statistike u realnom vremenu kako bismo dobili npr. skup jednistvenih IP adresa.
  3. Sortirani setovi dolaze do izražaja kada imamo nekakvo rangiranje i bodovanje. U deliću sekunde dobijamo poziciju na osnovu bodova, poziciju u odnosu na rezultat, korisnike u rasponu rezultata itd. Dakle idealno je rešenje kada nam treba lista rezultata u nekoj igrici na dnevnom, sedmičnom, godišnjem nivou ili npr. nekakve rezultate glasanja i ankete.
  4. Heševi pokazuju svoju prednost u očuvanju memorije. Ukoliko imamo 10000 heševa sa po pet elemenata na jednoj strani i 50000 string ključeva na drugoj strani, ovih 10000 heševa će zauzimati 4 puta manje memorije. To znači da kada imamo skup srodnih podataka u nekom modelu da treba koristiti Heš kao skup stringova pre nego stringove posebno.
  5. Liste imaju mnogostruku primenu. Naime svugde se susrećemo sa odredjenim listama čekanja i redovima. Upravo zato nam služe liste kako bi lako došli do podataka o prvih N elemenata ili poslednjih N elemenata.

Zaključak

Na kraju ne mogu reći da vreme Redisa dolazi jer ono je već došlo. Koliko je Redis dobar svedoče brojni uspešni primeri reinženjeringa sajtova sa velikom posetom. Sada su sve oči uprte u ono što nas očekuje a po svemu sudeći naša očekivanja biće ispunjena i Redis će predstavljati neizostavan činilac najposećenijih sajtova kao dokaz moći brzine i jednostavnosti u isto vreme.

3 thoughts on “Redis – moć brzine i jednostavnosti

  1. melin say:

    Impresivno, dakako…
    No, kod mene je to izazvalo izvesnu depresiju i pitanje: kako nastaviti dalje ziveti u mraku apsolutnog neznanja bilo cega o Redisu, Instagramu, Pinterest, Youporn, Tumblr, StackOverflow, Disqus, Guardian, Github, Blizzard..
    Da li je ukupan broj ljudi koji razumeju ovo o cemu pises ogranicen na broj dva na deseti -1?

    • komita1981 say:

      Citiraću nepoznatog autora : “Ko se bavi programiranjem a u 2011. godini a nije čuo za NOSQL taj mora da je živeo u pećini”. Promene su stalne moramo im se prilagođavati i neprestano učiti.

  2. Dusan Lukic say:

    Sjajno, mnogo korisnih informacija. Nastavicu da pratim blog i dopunjavam svoje znanje o Redisu. Svaka cast.

Leave a Reply

Your email address will not be published. Required fields are marked *