Liste over alle tilgængelige Redis-taster

Java Top

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN

1. Oversigt

Samlinger er en vigtig byggesten, der typisk ses i næsten alle moderne applikationer. Så det er ingen overraskelse Redis tilbyder en række populære datastrukturer såsom lister, sæt, hashes og sorterede sæt, som vi kan bruge.

I denne vejledning lærer vi, hvordan vi effektivt kan læse alle tilgængelige Redis-taster, der matcher et bestemt mønster.

2. Udforsk samlinger

Lad os forestille os, at vores applikationen bruger Redis til at gemme oplysninger om bolde bruges i forskellige sportsgrene. Vi burde være i stand til at se oplysninger om hver bold, der er tilgængelig fra Redis-samlingen. For enkelheds skyld begrænser vi vores datasæt til kun tre bolde:

  • Cricketbold med en vægt på 160 g
  • Fodbold med en vægt på 450 g
  • Volleyball med en vægt på 270 g

Lad os som sædvanligt rydde vores grundlæggende ved at arbejde på en naiv tilgang til at udforske Redis-samlinger.

3. Naiv tilgang ved hjælp af redis-cli

Før vi begynder at skrive Java-kode for at udforske samlingerne, skal vi have en god idé om, hvordan vi gør det ved hjælp af redis-cli interface. Lad os antage, at vores Redis-forekomst er tilgængelig på 127.0.0.1 på havn 6379, for at vi kan udforske hver samlingstype med kommandolinjegrænsefladen.

3.1. Tilknyttet liste

Lad os først opbevare vores datasæt på en navngivet Redis-linket liste bolde i formatet sports-navn_boldvægt ved hjælp af rpush kommando:

% redis-cli -h 127.0.0.1 -p 6379 127.0.0.1:6379> RPUSH bolde "cricket_160" (heltal) 1 127.0.0.1:6379> RPUSH bolde "fodbold_450" (heltal) 2 127.0.0.1:6379> RPUSH bolde "volleyball_270" (heltal) 3

Det kan vi bemærke en vellykket indsættelse i listen udsender den nye længde på listen. I de fleste tilfælde vil vi dog være blinde for dataindsættelsesaktiviteten. Som et resultat kan vi finde ud af længden på den linkede liste ved hjælp af llen kommando:

127.0.0.1:6379> llen-kugler (heltal) 3

Når vi allerede kender længden af ​​listen, er det praktisk at brug orden kommando for at hente hele datasættet let:

127.0.0.1:6379> række bolde 0 2 1) "cricket_160" 2) "fodbold_450" 3) "volleyball_270"

3.2. Sæt

Lad os derefter se, hvordan vi kan udforske datasættet, når vi beslutter at gemme det i et Redis-sæt. For at gøre det skal vi først udfylde vores datasæt i et Redis-sæt med navne bolde ved hjælp af trist kommando:

127.0.0.1:6379> sadler "cricket_160" "fodbold_450" "volleyball_270" "cricket_160" (heltal) 3

Ups! Vi havde en dobbelt værdi i vores kommando. Men da vi tilføjede værdier til et sæt, behøver vi ikke bekymre os om duplikater. Naturligvis kan vi se antallet af varer tilføjet fra outputresponsværdien.

Nu kan vi udnytte smembers kommando for at se alle de indstillede medlemmer:

127.0.0.1:6379> husker bolde 1) "volleyball_270" 2) "cricket_160" 3) "fodbold_450"

3.3. Hash

Lad os nu bruge Rediss hash-datastruktur til at gemme vores datasæt i en hash-nøgle med navnet bolde, således at hashs felt er sportsnavnet, og feltværdien er boldens vægt. Vi kan gøre dette ved hjælp af hmset kommando:

127.0.0.1:6379> hmset bolde cricket 160 fodbold 450 volleyball 270 OK

For at se de oplysninger, der er gemt i vores hash, kan vi brug hgetall kommando:

127.0.0.1:6379> hgetall bolde 1) "cricket" 2) "160" 3) "fodbold" 4) "450" ​​5) "volleyball" 6) "270"

3.4. Sorteret sæt

Ud over en unik medlemsværdi giver sorterede sæt os mulighed for at holde en score ved siden af ​​dem. I vores brugstilfælde kan vi holde sportens navn som medlemsværdi og boldens vægt som score. Lad os bruge zadd kommando til at gemme vores datasæt:

127.0.0.1:6379> zadd bolde 160 cricket 450 fodbold 270 volleyball (heltal) 3

Nu kan vi først bruge zcard kommando for at finde længden på det sorterede sæt efterfulgt af zrange kommando for at udforske det komplette sæt:

127.0.0.1:6379> zcard bolde (heltal) 3 127.0.0.1:6379> zrange bolde 0 2 1) "cricket" 2) "volleyball" 3) "fodbold"

3.5. Strenge

Vi kan også se sædvanlige nøgleværdistrenge som en overfladisk samling af genstande. Lad os først udfylde vores datasæt ved hjælp af mset kommando:

127.0.0.1:6379> mset bolde: cricket 160 bolde: fodbold 450 bolde: volleyball 270 OK

Vi skal bemærke, at vi tilføjede præfikset “bolde:så vi kan identificere disse nøgler fra resten af ​​nøglerne, der muligvis ligger i vores Redis-database. Desuden giver denne navngivningsstrategi os mulighed for at bruge nøgler kommando til at udforske vores datasæt ved hjælp af matchning af præfiksmønstre:

127.0.0.1:6379> nøgler bolde * 1) "bolde: cricket" 2) "bolde: volleyball" 3) "bolde: fodbold"

4. Naiv Java-implementering

Nu hvor vi har udviklet en grundlæggende idé om de relevante Redis-kommandoer, som vi kan bruge til at udforske samlinger af forskellige typer, er det tid for os at gøre vores hænder beskidte med kode.

4.1. Maven afhængighed

I dette afsnit vil vi være bruger Jedis klientbibliotek til Redis i vores implementering:

 redis.clients jedis 3.2.0 

4.2. Redis-klient

Jedis-biblioteket leveres med Redis-CLI-navnelignende metoder. Det anbefales dog, at vi Opret en wrapper Redis-klient, som internt påberåber Jedis-funktionskald.

Når vi arbejder med Jedis-biblioteket, skal vi huske det en enkelt Jedis-forekomst er ikke trådsikker. Derfor kan vi for at få en Jedis-ressource i vores applikation gøre brug af JedisPool, som er en trådsikker pool af netværksforbindelser.

Og da vi ikke vil have flere forekomster af Redis-klienter, der flyder rundt på et givet tidspunkt i vores applikations livscyklus, skal vi oprette vores RedisClient klasse på princippet om singleton design mønster.

Lad os først oprette en privat konstruktør til vores klient, der initialiserer internt JedisPool når en forekomst af RedisClient klasse oprettes:

privat statisk JedisPool jedisPool; privat RedisClient (streng ip, int-port) {prøv {hvis (jedisPool == null) {jedisPool = ny JedisPool (ny URI ("//" + ip + ":" + port)); }} fangst (URISyntaxException e) {log.error ("Fejlformet serveradresse", e); }}

Dernæst har vi brug for et adgangspunkt til vores singleton-klient. Så lad os oprette en statisk metode getInstance () til dette formål:

privat statisk flygtig RedisClient-forekomst = null; offentlig statisk RedisClient getInstance (streng ip, endelig int-port) {hvis (forekomst == null) {synkroniseret (RedisClient.class) {hvis (forekomst == null) {forekomst = ny RedisClient (ip, port); }}} returner instans; }

Lad os endelig se, hvordan vi kan oprette en indpakningsmetode oven på Jedis Område metode:

public List lrange (final String key, final long start, final long stop) {try (Jedis jedis = jedisPool.getResource ()) {return jedis.lrange (key, start, stop); } fange (Undtagelse ex) {log.error ("Undtagelse fanget i område", ex); } returner ny LinkedList (); }

Selvfølgelig kan vi følge den samme strategi for at skabe resten af ​​indpakningsmetoderne som f.eks lpush, hmset, hgetall, trist, smembers, nøgler, zaddog zrange.

4.3. Analyse

Alle Redis-kommandoer, som vi kan bruge til udforske en samling på en gang vil naturligvis have en O (n) tidskompleksitet i bedste fald.

Vi er måske lidt liberale og kalder denne tilgang som naiv. I en virkelig produktionsinstans af Redis er det ret almindeligt at have tusinder eller millioner af nøgler i en enkelt samling. Desuden bringer Redis 'single-threaded nature mere elendighed, og vores tilgang kan katastrofalt blokere for andre operationer med højere prioritet.

Så vi skal gøre det til et punkt, at vi begrænser vores naive tilgang til kun at blive brugt til fejlfindingsformål.

5. Grundlæggende om itterator

Den største fejl i vores naive implementering er, at vi anmoder Redis om at give os alle resultaterne for vores enkelt hentningsforespørgsel på én gang. For at løse dette problem kan vi bryde vores originale henteforespørgsel i flere sekventielle hentningsforespørgsler, der fungerer på mindre stykker af hele datasættet.

Lad os antage, at vi har en bog på 1.000 sider, som vi skal læse. Hvis vi følger vores naive tilgang, bliver vi nødt til at læse denne store bog i et enkelt møde uden pauser. Det vil være fatalt for vores velbefindende, da det dræner vores energi og forhindrer os i at udføre andre aktiviteter med højere prioritet.

Selvfølgelig er den rigtige måde at afslutte bogen over flere læsesessioner. I hver session vi genoptager hvor vi slap i den forrige session - vi kan spore vores fremskridt ved hjælp af et sidebogmærke.

Selv om den samlede aflæsningstid i begge tilfælde vil være af sammenlignelig værdi, er den anden tilgang ikke desto mindre bedre, da den giver os plads til at trække vejret.

Lad os se, hvordan vi kan bruge en iteratorbaseret tilgang til at udforske Redis-samlinger.

6. Redis-scanning

Redis tilbyder flere scanningsstrategier for at læse nøgler fra samlinger ved hjælp af en markørbaseret tilgang, der i princippet svarer til et sidebogmærke.

6.1. Scanningsstrategier

Vi kan scanne gennem hele nøgleværdisamlingsbutikken ved hjælp af Scanning kommando. Men hvis vi vil begrænse vores datasæt efter samlingstyper, kan vi bruge en af ​​varianterne:

  • Sscan kan bruges til iterering gennem sæt
  • Hscan hjælper os med at gentage gennem par feltværdi i en hash
  • Zscan tillader en iteration gennem medlemmer, der er gemt i et sorteret sæt

Vi skal bemærke, at vi behøver ikke rigtig brug af en scanningsstrategi på serversiden, der er specielt designet til de linkede lister. Det skyldes, at vi kan få adgang til medlemmer af den linkede liste gennem indekser ved hjælp af lindex eller orden kommando. Plus, vi kan finde ud af antallet af elementer og brug orden i en simpel sløjfe for at gentage hele listen i små bidder.

Lad os bruge SCANNING kommando til at scanne over nøgler af strengtype. For at starte scanningen skal vi bruge markørværdien som "0", matchende mønsterstreng som "kugle *":

127.0.0.1:6379> mset bolde: cricket 160 bolde: fodbold 450 bolde: volleyball 270 OK 127.0.0.1:6379> SCAN 0 MATCH bold * TÆLL 1 1) "2" 2) 1) "bolde: cricket" 127.0.0.1 : 6379> SCAN 2 MATCH bold * TÆLL 1 1) "3" 2) 1) "bolde: volleyball" 127.0.0.1:6379> SCAN 3 MATCH bold * ANTAL 1 1) "0" 2) 1) "bolde: fodbold "

For hver afsluttet scanning får vi den næste markørværdi, der skal bruges i den efterfølgende iteration. Til sidst ved vi, at vi har scannet gennem hele samlingen, når den næste markørværdi er "0".

7. Scanning med Java

Nu har vi tilstrækkelig forståelse for vores tilgang til, at vi kan begynde at implementere den i Java.

7.1. Scanningsstrategier

Hvis vi kigger ind i kernens scanningsfunktionalitet, der tilbydes af Jedis klasse finder vi strategier til at scanne forskellige samlingstyper:

offentlig ScanResult-scanning (endelig strengmarkør, endelig ScanParams-parameter); offentlig ScanResult sscan (endelig strengnøgle, endelig strengmarkør, endelig ScanParams-parameter); offentlig ScanResult hscan (sidste strengnøgle, sidste strengmarkør, endelige ScanParams-parametre); offentlig ScanResult zscan (endelig strengnøgle, endelig strengmarkør, endelig ScanParams-parameter);

Jedis kræver to valgfri parametre, søgemønster og resultatstørrelse til effektiv kontrol af scanningen - ScanParams får dette til at ske. Til dette formål er det afhængigt af match() og tælle() metoder, der er løst baseret på byggeriets designmønster:

offentlig ScanParams-kamp (endelig strengmønster); offentligt antal ScanParams (endelig optælling af heltal);

Nu hvor vi har gennemblødt den grundlæggende viden om Jedis's scanningstilgang, lad os modellere disse strategier gennem en ScanStrategy grænseflade:

offentlig grænseflade ScanStrategy {ScanResult scan (Jedis jedis, String cursor, ScanParams scanParams); }

Lad os først arbejde med det enkleste scanning strategi, som er uafhængig af samlingstypen og læser nøglerne, men ikke nøglenes værdi:

offentlig klasse Scan implementerer ScanStrategy {public ScanResult scan (Jedis jedis, String cursor, ScanParams scanParams) {return jedis.scan (cursor, scanParams); }}

Lad os derefter hente hscan strategi, som er skræddersyet til at læse alle feltnøglerne og feltværdierne for en bestemt hash-nøgle:

offentlig klasse Hscan implementerer ScanStrategy {privat strengnøgle; @ Overstyr offentlig ScanResult scan (Jedis jedis, String cursor, ScanParams scanParams) {return jedis.hscan (key, cursor, scanParams); }}

Lad os endelig bygge strategierne for sæt og sorterede sæt. Det sscan strategi kan læse alle medlemmerne af et sæt, mens zscan strategi kan læse medlemmerne sammen med deres score i form af Tuples:

offentlig klasse Sscan implementerer ScanStrategy {privat strengnøgle; offentlig ScanResult-scanning (Jedis jedis, strengmarkør, ScanParams scanParams) {returner jedis.sscan (nøgle, markør, scanParams); }} offentlig klasse Zscan implementerer ScanStrategy {privat strengnøgle; @ Override offentlig ScanResult-scanning (Jedis jedis, strengmarkør, ScanParams scanParams) {returner jedis.zscan (nøgle, markør, scanParams); }}

7.2. Redis Iterator

Lad os derefter skitsere de byggesten, der er nødvendige for at bygge vores RedisIterator klasse:

  • Strengbaseret markør
  • Scanningsstrategi som f.eks scanning, sscan, hscan, zscan
  • Pladsholder til scanning af parametre
  • Adgang til JedisPool at få en Jedis ressource

Vi kan nu gå videre og definere disse medlemmer i vores RedisIterator klasse:

privat final JedisPool jedisPool; private ScanParams scanParams; privat strengmarkør; privat ScanStrategy-strategi;

Vores scene er klar til at definere den iteratorspecifikke funktionalitet til vores iterator. For det, vores RedisIterator klasse skal implementere Iterator grænseflade:

offentlig klasse RedisIterator implementerer Iterator { }

Naturligvis kræves det, at vi tilsidesætter hasNext () og Næste() metoder arvet fra Iterator interface.

Lad os først plukke den lavhængende frugt - den hasNext () metode - da den underliggende logik er ligetil. Så snart markørværdien bliver “0”, ved vi, at vi er færdige med scanningen. Så lad os se, hvordan vi kan implementere dette på kun en linje:

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

Lad os derefter arbejde på Næste() metode, der gør det tunge løft ved scanning:

@Override public List næste () {if (cursor == null) {cursor = "0"; } prøv (Jedis jedis = jedisPool.getResource ()) {ScanResult scanResult = strategi.scan (jedis, markør, scanParams); markør = scanResult.getCursor (); returner scanResult.getResult (); } fange (Undtagelse ex) {log.error ("Undtagelse fanget i næste ()", ex); } returner ny LinkedList (); }

Det skal vi bemærke ScanResult giver ikke kun de scannede resultater, men også den næste markørværdi behov for den efterfølgende scanning.

Endelig kan vi aktivere funktionaliteten til at oprette vores RedisIterator i RedisClient klasse:

offentlig RedisIterator-iterator (int initialScanCount, strengemønster, ScanStrategy-strategi) {returner ny RedisIterator (jedisPool, initialScanCount, mønster, strategi); }

7.3. Læs med Redis Iterator

Som vi har designet vores Redis iterator ved hjælp af Iterator interface, det er ret intuitivt at læse samlingens værdier ved hjælp af Næste() metode så længe hasNext () vender tilbage rigtigt.

Af hensyn til fuldstændighed og enkelhed gemmer vi først datasættet relateret til sportsbolde i en Redis-hash. Derefter bruger vi vores RedisClient at oprette en iterator ved hjælp af Hscan scanningsstrategi. Lad os teste vores implementering ved at se dette i aktion:

@Test offentlig ugyldig testHscanStrategy () {HashMap hash = ny HashMap (); hash.put ("cricket", "160"); hash.put ("fodbold", "450"); hash.put ("volleyball", "270"); redisClient.hmset ("bolde", hash); Hscan scanStrategy = ny Hscan ("bolde"); int iterationCount = 2; RedisIterator iterator = redisClient.iterator (iterationCount, "*", scanStrategy); Liste resultater = ny LinkedList(); mens (iterator.hasNext ()) {results.addAll (iterator.next ()); } Assert.assertEquals (hash.size (), results.size ()); }

Vi kan følge den samme tankeproces med lidt ændring for at teste og implementere de resterende strategier for at scanne og læse de tilgængelige nøgler i forskellige typer samlinger.

8. Konklusion

Vi startede denne vejledning med en intention om at lære om, hvordan vi kan læse alle de matchende taster i Redis.

Vi fandt ud af, at Redis tilbyder en enkel måde at læse nøgler på én gang. Selvom det var simpelt, diskuterede vi, hvordan dette belaster ressourcerne og derfor ikke er egnet til produktionssystemer. Da vi gravede dybere, blev vi klar over, at der er en iteratorbaseret tilgang til scanning gennem matchende Redis-taster til vores læseforespørgsel.

Som altid er den komplette kildekode til Java-implementeringen, der bruges i denne artikel, tilgængelig på GitHub.

Java bund

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN

$config[zx-auto] not found$config[zx-overlay] not found