Popis svih dostupnih Redis tipki

Java Top

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ

1. Pregled

Kolekcije su bitan gradivni blok koji se obično vidi u gotovo svim modernim aplikacijama. Dakle, nije iznenađenje da Redis nudi razne popularne strukture podataka kao što su popisi, skupovi, heši i razvrstani skupovi koje ćemo koristiti.

U ovom uputstvu naučit ćemo kako možemo učinkovito pročitati sve dostupne Redis tipke koje odgovaraju određenom uzorku.

2. Istražite zbirke

Zamislimo da je naš aplikacija koristi Redis za pohranu podataka o loptama koristi se u različitim sportovima. Trebali bismo biti u mogućnosti vidjeti informacije o svakoj kuglici dostupne iz kolekcije Redis. Radi jednostavnosti, ograničit ćemo naš skup podataka na samo tri lopte:

  • Lopta za kriket težine 160 g
  • Nogomet s težinom od 450 g
  • Odbojka s težinom od 270 g

Kao i obično, prvo razjasnimo svoje osnove radeći na naivnom pristupu istraživanju Redis kolekcija.

3. Naivni pristup korištenjem redis-cli-a

Prije nego što započnemo s pisanjem Java koda za istraživanje kolekcija, trebali bismo imati dobru ideju kako ćemo to učiniti pomoću redis-cli sučelje. Pretpostavimo da je naša instanca Redis dostupna na 127.0.0.1 u luci 6379, da istražimo svaku vrstu zbirke pomoću sučelja naredbenog retka.

3.1. Povezani popis

Prvo, pohranimo svoj skup podataka u Redis povezan popis s imenom kuglice u formatu sportsko-ime_loptasta težina uz pomoć rpush naredba:

% redis-cli -h 127.0.0.1 -p 6379 127.0.0.1:6379> RPUSH loptice "kriket_160" (cijeli broj) 1 127.0.0.1:6379> RPUSH lopte "football_450" (cijeli broj) 2 127.0.0.1:6379> RPUSH lopte "odbojka_270" (cijeli broj) 3

To možemo primijetiti uspješno umetanje na popis daje novu duljinu popisa. Međutim, u većini slučajeva bit ćemo slijepi zbog aktivnosti umetanja podataka. Kao rezultat, možemo saznati duljinu povezanog popisa pomoću llen naredba:

127.0.0.1:6379> llen lopte (cijeli broj) 3

Kad već znamo duljinu popisa, prikladno je koristiti čudno naredba da biste lako preuzeli cjelokupni skup podataka:

127.0.0.1:6379> čudesne lopte 0 2 1) "kriket_160" 2) "nogomet_450" 3) "odbojka_270"

3.2. Postavi

Dalje, pogledajmo kako možemo istražiti skup podataka kada ga odlučimo pohraniti u Redis skup. Da bismo to učinili, prvo moramo popuniti svoj skup podataka u Redis skup nazvan kuglice pomoću sadd naredba:

127.0.0.1:6379> sadd lopte "kriket_160" "nogomet_450" "odbojka_270" "kriket_160" (cijeli broj) 3

Ups! U naredbi smo imali duplikat. No, budući da smo skupu dodavali vrijednosti, ne trebamo brinuti o duplikatima. Naravno, možemo vidjeti broj stavki dodanih iz izlazne vrijednosti odgovora.

Sad možemo iskoristiti smembers naredba za prikaz svih postavljenih članova:

127.0.0.1:6379> lopte za pamćenje 1) "odbojka_270" 2) "kriket_160" 3) "nogomet_450"

3.3. Hash

Sada, upotrijebimo Redisovu strukturu hash podataka za pohranu našeg skupa podataka u hash ključ nazvan lopte, tako da je hash polje sportsko ime, a vrijednost polja težina lopte. To možemo učiniti uz pomoć hmset naredba:

127.0.0.1:6379> hmset lopte kriket 160 nogomet 450 odbojka 270 OK

Da bismo vidjeli podatke pohranjene u našem hashu, možemo koristiti hgetall naredba:

127.0.0.1:6379> hgetall lopte 1) "kriket" 2) "160" 3) "nogomet" 4) "450" ​​5) "odbojka" 6) "270"

3.4. Razvrstani set

Pored jedinstvene vrijednosti člana, sortirani skupovi omogućuju nam da pored njih zadržimo i rezultat. Pa, u našem slučaju upotrebe, naziv sporta možemo zadržati kao člansku vrijednost, a težinu lopte kao rezultat. Iskoristimo zadd naredba za pohranu našeg skupa podataka:

127.0.0.1:6379> zadd lopte 160 kriket 450 nogomet 270 odbojka (cijeli broj) 3

Sada prvo možemo koristiti zcard naredba za pronalaženje duljine razvrstanog skupa, nakon čega slijedi zrange naredba za istraživanje kompletnog seta:

127.0.0.1:6379> zcard lopte (cijeli broj) 3 127.0.0.1:6379> zrange lopte 0 2 1) "kriket" 2) "odbojka" 3) "nogomet"

3.5. Žice

Također možemo vidjeti i uobičajeni nizovi ključ / vrijednost kao površna zbirka predmeta. Prvo popunimo svoj skup podataka pomoću mset naredba:

127.0.0.1:6379> mset lopte: kriket 160 lopti: nogomet 450 lopti: odbojka 270 OK

Moramo napomenuti da smo dodali prefiks „kuglice:tako da ove ključeve možemo prepoznati iz ostatka ključeva koji možda leže u našoj bazi podataka Redis. Štoviše, ova strategija imenovanja omogućuje nam upotrebu tipke naredba za istraživanje našeg skupa podataka uz pomoć podudaranja uzorka prefiksa:

127.0.0.1:6379> tipke lopte * 1) "lopte: kriket" 2) "lopte: odbojka" 3) "lopte: nogomet"

4. Naivna implementacija Jave

Sad kad smo razvili osnovnu ideju relevantnih naredbi Redis koje možemo koristiti za istraživanje zbirki različitih vrsta, vrijeme je da prljamo ruke kodom.

4.1. Ovisnost Mavena

U ovom ćemo dijelu biti koristiti Jediji knjižnica klijenta za Redis u našoj implementaciji:

 redis.klijenti jedis 3.2.0 

4.2. Klijent Redis

Biblioteka Jedis dolazi s Redis-CLI metodama sličnim imenima. Međutim, preporučuje se da stvoriti klijent omotača Redis, koji će interno pozivati ​​pozive funkcije Jedis.

Kad god radimo s bibliotekom Jedis, moramo to imati na umu pojedinačna Jedijeva instalacija nije sigurna u niti. Stoga, da bismo dobili resurs Jedija u našu aplikaciju, možemo iskoristiti JedisPool, koji je spremište bez niti mrežnih veza.

A budući da ne želimo da više instanci Redisovih klijenata plutaju u bilo kojem trenutku tijekom životnog ciklusa naše aplikacije, trebali bismo stvoriti RedisClient razred na principu jednobojnog dizajnerskog uzorka.

Prvo, kreirajmo privatni konstruktor za našeg klijenta koji će interno inicijalizirati JedisPool kada instanca od RedisClient klasa je stvorena:

privatni statički JedisPool jedisPool; private RedisClient (String ip, int port) {try {if (jedisPool == null) {jedisPool = novi JedisPool (novi URI ("//" + ip + ":" + port)); }} catch (URISyntaxException e) {log.error ("Neispravna adresa poslužitelja", e); }}

Dalje, trebamo pristupnu točku našem singleton klijentu. Dakle, stvorimo statičku metodu getInstance () za ovu svrhu:

privatna statička volatilna instanca RedisClient = null; javni statički RedisClient getInstance (string ip, završni int port) {if (instance == null) {sinkronizirano (RedisClient.class) {if (instance == null) {instance = new RedisClient (ip, port); }}} return instanca; }

Napokon, pogledajmo kako možemo stvoriti metodu omota povrh Jedijevih čudna metoda:

javni popis lrange (konačni ključ niza, konačni dugi početak, krajnji dugi stop) {try (Jedis jedis = jedisPool.getResource ()) {return jedis.lrange (ključ, početak, zaustavljanje); } catch (Iznimka ex) {log.error ("Iznimka uhvaćena u lrange", ex); } vrati novi LinkedList (); }

Naravno, možemo slijediti istu strategiju kako bismo stvorili ostatak metoda omota poput lpush, hmset, hgetall, sadd, smembers, tipke, zadd, i zrange.

4.3. Analiza

Sve naredbe Redis na koje se možemo koristiti istražite kolekciju u jednom potezu, u najboljem slučaju prirodno će imati vremensku složenost O (n).

Možda smo pomalo liberalni, nazivajući ovaj pristup naivnim. U stvarnoj proizvodnoj instanci Redisa uobičajeno je imati tisuće ili milijune ključeva u jednoj kolekciji. Nadalje, Redisova jednonitna priroda donosi više bijede, a naš bi pristup mogao katastrofalno blokirati druge operacije s višim prioritetom.

Dakle, trebali bismo istaknuti da ograničavamo naš naivni pristup da se koristi samo u svrhe otklanjanja pogrešaka.

5. Osnove iteratora

Glavni nedostatak naše naivne implementacije je taj što tražimo od Redisa da nam u jednom potezu pruži sve rezultate za naš pojedinačni upit. Da bismo prevladali ovaj problem, izvorni upit za dohvaćanje možemo razbiti na više upita za uzastopno dohvaćanje koji djeluju na manjim dijelovima cijelog skupa podataka.

Pretpostavimo da imamo knjigu od 1000 stranica koju bismo trebali pročitati. Ako slijedimo naš naivan pristup, morat ćemo pročitati ovu veliku knjigu u jednom sjedenju bez ikakvih pauza. To će biti kobno za našu dobrobit, jer će nam iscrpiti energiju i spriječiti nas u obavljanju bilo koje druge aktivnosti višeg prioriteta.

Naravno, pravi način je završiti knjigu tijekom više sesija čitanja. U svakoj sesiji, nastavljamo od tamo gdje smo stali u prethodnoj sesiji - možemo pratite naš napredak pomoću oznake stranice.

Iako će ukupno vrijeme čitanja u oba slučaja biti usporedive vrijednosti, ipak je drugi pristup bolji jer nam daje prostor za disanje.

Pogledajmo kako možemo koristiti pristup zasnovan na iteratoru za istraživanje Redis kolekcija.

6. Redis Scan

Redis nudi nekoliko strategija skeniranja za čitanje tipki iz zbirki pomoću pristupa koji se temelji na kurzoru, a koji je u principu sličan oznaci stranice.

6.1. Strategije skeniranja

Možemo skenirati cijelu trgovinu zbirki ključeva i vrijednosti pomoću datoteke Skenirati naredba. Međutim, ako želimo ograničiti svoj skup podataka vrstama zbirki, tada možemo koristiti jednu od varijanti:

  • Sscan može se koristiti za itiranje kroz skupove
  • Hscan pomaže nam u iteraciji kroz parove polja-vrijednosti u hashu
  • Zscan omogućuje iteraciju kroz članove pohranjene u razvrstanom skupu

Moramo napomenuti da mi zapravo ne trebaju strategiju skeniranja na strani poslužitelja posebno dizajniranu za povezane popise. To je zato što članovima povezanog popisa možemo pristupiti putem indeksa pomoću alata lindex ili čudno naredba. Osim toga, možemo saznati broj elemenata i upotrebu čudno u jednostavnoj petlji za ponavljanje cijelog popisa u malim dijelovima.

Iskoristimo SKENIRATI naredba za skeniranje tipki niza. Da bismo započeli skeniranje, trebamo upotrijebiti vrijednost kursora kao "0", podudaranje niza uzorka kao “lopta *”:

127.0.0.1:6379> mset lopte: kriket 160 lopti: nogomet 450 lopte: odbojka 270 OK 127.0.0.1:6379> SCAN 0 MATCH lopta * COUNT 1 1) "2" 2) 1) "lopte: kriket" 127.0.0.1 : 6379> SCAN 2 MATCH ball * COUNT 1 1) "3" 2) 1) "lopte: odbojka" 127.0.0.1:6379> SCAN 3 MATCH lopta * COUNT 1 1) "0" 2) 1) "lopte: nogomet "

Svakim završenim skeniranjem dobivamo sljedeću vrijednost pokazivača koja će se koristiti u sljedećoj iteraciji. Na kraju znamo da smo pregledali cijelu kolekciju kada je sljedeća vrijednost pokazivača "0".

7. Skeniranje s Javom

Do sada imamo dovoljno razumijevanja za svoj pristup da ga možemo početi implementirati u Javi.

7.1. Strategije skeniranja

Ako zavirimo u osnovnu funkciju skeniranja koju nudi Jediji klase, pronaći ćemo strategije za skeniranje različitih vrsta zbirki:

javno skeniranje rezultata ScanResult (završni pokazivač niza, konačni parametri ScanParams); javni ScanResult sscan (završni ključ String, završni kursor Stringa, konačni parametri ScanParams); javni ScanResult hscan (završni ključ Stringa, završni pokazivač Stringa, konačni parametri ScanParams); javni ScanResult zscan (završni ključ String, završni pokazivač Stringa, konačni parametri ScanParams);

Jediji zahtijeva dva neobavezna parametra, obrazac pretraživanja i veličina rezultata, za učinkovitu kontrolu skeniranja - ScanParams čini da se ovo dogodi. U tu se svrhu oslanja na podudaranje () i računati() metode koje se labavo temelje na uzorku dizajna graditelja:

javno podudaranje ScanParams (konačni uzorak niza); javni broj ScanParams (konačni broj cijelog broja);

Sad kad smo se natopili osnovnim znanjem o Jedijeva pristup skeniranju, modelirajmo ove strategije kroz ScanStrategy sučelje:

javno sučelje ScanStrategy {ScanResult scan (Jedis jedis, String cursor, ScanParams scanParams); }

Prvo, poradimo na najjednostavnijem skenirati strategija, koja je neovisna o tipu kolekcije i čita ključeve, ali ne i vrijednost ključeva:

javna klasa Scan implementira ScanStrategy {javni ScanResult skeniranje (Jedis jedis, String cursor, ScanParams scanParams) {return jedis.scan (cursor, scanParams); }}

Dalje, pokupimo hscan strategija, koja je prilagođena čitanju svih ključeva polja i vrijednosti polja određenog hash ključa:

javna klasa Hscan implementira ScanStrategy {privatni ključ niza; @Preuzmi javni ScanResult scan (Jedis jedis, String cursor, ScanParams scanParams) {return jedis.hscan (key, cursor, scanParams); }}

Na kraju, izgradimo strategije za skupove i razvrstane skupove. The sscan strategija može čitati sve članove skupa, dok zscan strategija može čitati članove zajedno s njihovim ocjenama u obliku Korijens:

javna klasa Sscan implementira ScanStrategy {private String key; javno skeniranje rezultata ScanResult (Jedis jedis, String cursor, ScanParams scanParams) {return jedis.sscan (key, cursor, scanParams); }} javna klasa Zscan implementira ScanStrategy {private String key; @Override public ScanResult scan (Jedis jedis, String cursor, ScanParams scanParams) {return jedis.zscan (key, cursor, scanParams); }}

7.2. Redis Iterator

Dalje, skicirajmo građevne blokove potrebne za izgradnju našeg RedisIterator razred:

  • Kursor zasnovan na nizu
  • Strategija skeniranja kao što je skenirati, sscan, hscan, zscan
  • Rezervirano mjesto za parametre skeniranja
  • Pristup JedisPool dobiti a Jediji resurs

Sada možemo naprijed i definirati te članove u našem RedisIterator razred:

privatni konačni JedisPool jedisPool; privatni ScanParams scanParams; pokazivač privatnog niza; privatna strategija ScanStrategy;

Naša je faza spremna za definiranje funkcionalnosti specifične za iterator za naš iterator. Za to, naš RedisIterator klasa mora implementirati Iterator sučelje:

javna klasa RedisIterator implementira Iterator { }

Naravno, od nas se traži da poništimo hasNext () i Sljedeći() metode naslijeđene od Iterator sučelje.

Prvo, uberimo slabo visi voće - hasNext () metoda - jer je temeljna logika izravna. Čim vrijednost kursora postane "0", znamo da smo gotovi sa skeniranjem. Pa, pogledajmo kako to možemo implementirati u samo jedan redak:

@Override public boolean hasNext () {return! "0" .equals (kursor); }

Dalje, poradimo na Sljedeći() metoda koja vrši teško dizanje skeniranja:

@Preuzmi javni popis next () {if (cursor == null) {cursor = "0"; } probajte (Jedis jedis = jedisPool.getResource ()) {ScanResult scanResult = strategy.scan (jedis, kursor, scanParams); kursor = scanResult.getCursor (); vratiti scanResult.getResult (); } catch (Iznimka ex) {log.error ("Iznimka uhvaćena u next ()", ex); } vrati novi LinkedList (); }

To moramo primijetiti ScanResult ne daje samo skenirane rezultate već i sljedeću vrijednost kursora potreban za naknadno skeniranje.

Napokon, možemo omogućiti funkcionalnost za stvaranje našeg RedisIterator u RedisClient razred:

javni iterator RedisIterator (int početniScanCount, uzorak niza, strategija ScanStrategy) {vrati novi RedisIterator (jedisPool, InitiScanCount, uzorak, strategija); }

7.3. Čitajte s Redis Iteratorom

Kao što smo dizajnirali naš Redis iterator uz pomoć Iterator sučelje, prilično je intuitivno za pročitajte vrijednosti zbirke uz pomoć Sljedeći() metoda sve dok hasNext () vraća se pravi.

Radi cjelovitosti i jednostavnosti, skup podataka koji se odnose na sportske lopte prvo ćemo pohraniti u Redisov heš. Nakon toga koristit ćemo naš RedisClient za stvaranje iteratora pomoću Hscan strategija skeniranja. Isprobajmo našu implementaciju tako što ćemo vidjeti ovo na djelu:

@Test public void testHscanStrategy () {HashMap hash = novi HashMap (); hash.put ("cvrčak", "160"); hash.put ("nogomet", "450"); hash.put ("odbojka", "270"); redisClient.hmset ("kuglice", hash); Hscan scanStrategy = novi Hscan ("kuglice"); int iterationCount = 2; RedisIterator iterator = redisClient.iterator (iterationCount, "*", scanStrategy); Popis rezultati = novi LinkedList(); while (iterator.hasNext ()) {results.addAll (iterator.next ()); } Assert.assertEquals (hash.size (), results.size ()); }

Možemo slijediti isti proces razmišljanja s malo izmjena kako bismo testirali i implementirali preostale strategije skeniranja i čitanja ključeva dostupnih u različitim vrstama zbirki.

8. Zaključak

Ovaj smo vodič započeli s namjerom da naučimo kako možemo pročitati sve odgovarajuće tipke u Redisu.

Otkrili smo da Redis nudi jednostavan način čitanja tipki u jednom potezu. Iako jednostavni, razgovarali smo o tome kako ovo opterećuje resurse i stoga nije pogodno za proizvodne sustave. Kopajući dublje, saznali smo da postoji pristup skeniranju zasnovan na iteratoru kroz podudaranje Redisovih tipki za naš upit za čitanje.

Kao i uvijek, cjeloviti izvorni kod za implementaciju Jave korišten u ovom članku dostupan je na GitHub-u.

Dno Java

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ