Redis-hashes zijn (intuïtief genoeg!) hashes die stringnamen toewijzen aan stringwaarden. Het zijn in wezen containers met unieke velden en hun waarden. Ze zijn de perfecte manier om een object weer te geven als een Redis-gegevensstructuur. Zoals verwacht, bieden ze basisbewerkingen met constante tijd, zoals ophalen, instellen, bestaat enz. Er wordt ook een aantal geavanceerde bewerkingen geboden. De volledige lijst met hash-commando's vindt u hier.
Laten we eens een rondje maken vanaf de redis-cli .
# hmset key field value [field value ...] : Insert elements in a hash. O(N), N is # of field being set 127.0.0.1:6379> hmset std:101 name "John Smith" dob "01-01-2000" gender M active 0 cgpa 2.9 OK # hgetall key : key all keys and values in the hash. O(N), N is size of hash 127.0.0.1:6379> hgetall std:101 1) "name" 2) "John Smith" 3) "dob" 4) "01-01-2000" 5) "gender" 6) "M" 7) "active" 8) "0" 9) "cgpa" 10) "2.9" 127.0.0.1:6379> hmset std:102 name "Jane" name "Ann" OK # If duplicates are found, only the last set is valid 127.0.0.1:6379> hgetall std:102 1) "name" 2) "Ann" # hexists key field: does field exist in the hash with key. O(1) 127.0.0.1:6379> hexists std:102 cgpa (integer) 0 # hincrby key field increment: Increment the integer field by increment. O(1) 127.0.0.1:6379> hincrby std:101 active 1 (integer) 1 # hget key field : the value for field in the hash stored at key. O(1) 127.0.0.1:6379> hget std:101 active 1) "1" # If field doesn't exist, hincrby sets it to 0 and then applies increment 127.0.0.1:6379> hincrby std:102 active 2 (integer) 2 # hmget key field [field ...]: the values of the fields requested for the hash with key. O(N), N is # of keys requested 127.0.0.1:6379> hmget std:102 active 1) "2" # hincrbyfloat key field increment: Increment the float value in field by increment. O(1) 127.0.0.1:6379> HINCRBYFLOAT std:101 cgpa 1.0 "3.9" # HSETNX key field value: set field to value if not alread set. O(1) 127.0.0.1:6379> hsetnx std:102 cgpa 4.0 (integer) 1 127.0.0.1:6379> hget std:102 cgpa "4.0" # hlen key: number of fields in the hash. O(1) 127.0.0.1:6379> hlen std:101 (integer) 5 # hkeys key : all fields in the hash. O(N), N is size of hash 127.0.0.1:6379> hkeys std:101 1) "name" 2) "dob" 3) "gender" 4) "active" 5) "cgpa"
Zoals we gewend zijn van onze hosting voor Redis™* als datastructuurserver, zien we dat Redis redelijk bruikbare en geavanceerde bewerkingen op hashes biedt.
Intern
Net als Redis-sets worden ook Redis-hashes geïmplementeerd als woordenboeken. Woordenboeken in Redis worden geïmplementeerd als hash-tabellen die de hash-functie MurmurHash2 gebruiken en groeien via incrementele resizing. Hash-botsingen worden afgehandeld door middel van chaining. Meer details zijn te vinden in de Redis-implementatie van het woordenboek op dict.c.
Net als bij Sets is er opslagoptimalisatie gemaakt voor kleinere hashes. Deze gegevensstructuur wordt ziplist genoemd (hashes werden geoptimaliseerd met behulp van een andere gegevensstructuur, zipmap genaamd, vóór Redis 2.6) in de Redis-implementatie. Het is in wezen een speciaal gecodeerde dubbel gelinkte lijst die is geoptimaliseerd voor geheugenbesparing. De gegevens, evenals de wijzers worden inline opgeslagen. Ziplist wordt ook gebruikt om de opslag van kleinere gesorteerde sets en lijsten te optimaliseren. Een hash die in zo'n lijst wordt samengevoegd, ziet er ongeveer zo uit, [key1, value1, key2, value2, ...]. Hoe is dit efficiënter dan gewone toetsen? Hashes met weinig sleutels kunnen slim in deze lineaire array-achtige structuur (d.w.z. de ziplist) worden ingepakt, terwijl ze nog steeds de afgeschreven O(1)-prestaties voor get en set garanderen. Uiteraard kan dit niet bijbenen naarmate de hash-velden toenemen. Naarmate de hash groeit, wordt deze omgezet in de standaard woordenboekstructuur om de O(1)-prestaties te behouden en gaat de ruimtebesparing verloren. Redis-configuratieparameters die deze transformatie besturen, zijn:
- list-max-ziplist-entries standaard (512):Wijzig naar standaardweergave als hash groter wordt dan deze limiet.
- list-max-ziplist-value standaard (64):Wijzig naar standaardweergave als het grootste element in de hash groter wordt dan deze limiet.
Meer details zijn te vinden in de code en opmerkingen in de implementatie die hier te vinden zijn. De geheugenbesparingen door het gebruik van deze speciale optimalisatie zijn aanzienlijk. We zullen er in de volgende sectie meer over vertellen.
Geheugenoptimalisatie
Een van de bekende aanbevelingen voor geheugenbesparing tijdens het gebruik van Redis is om hashes te gebruiken in plaats van gewone strings. Dit is een belangrijke use-case voor het gebruik van de kracht van Redis-hashes in toepassingen in de echte wereld. Uit de officiële Redis-documentatie over geheugenoptimalisatie:
Gebruik waar mogelijk hashes
Kleine hashes zijn gecodeerd in een zeer kleine ruimte, dus je moet proberen om je gegevens elke keer dat mogelijk is met hashes weer te geven. Als u bijvoorbeeld objecten heeft die gebruikers vertegenwoordigen in een webtoepassing, in plaats van verschillende sleutels te gebruiken voor naam, achternaam, e-mail, wachtwoord, gebruikt u een enkele hash met alle vereiste velden.
Dat bericht stelt vervolgens een manier voor om een reeks objecten in een set hashes in kaart te brengen om te profiteren van de geheugenbesparing. Instagram beschrijft in een zeer populaire blogpost het gebruik van een vergelijkbare techniek die hen hielp om grote besparingen te realiseren. Een andere blog die de voordelen van de optimalisatie probeert te meten, is deze.
Toepassingen
- Redis-hashes zijn van nature geschikt om objecten op te slaan:sessies, gebruikers, bezoekers enz. Dit maakt het een van de belangrijkste gegevensstructuren van Redis.
- In zijn voor geheugen geoptimaliseerde vorm is het een uitstekende keuze voor het cachen van grote hoeveelheden gegevens.
Een objectadreswinkel
Omdat geheugenoptimalisatie een belangrijk gebruiksscenario is voor hashes, bespreken we een voorbeeld dat vergelijkbaar is met de Instagram-implementatie om te laten zien hoe geheugenbesparende functies van Redis-hashes kunnen worden gebruikt. Laten we zeggen dat we een enorme content-addressable storage (CAS)-implementatie hebben met honderden miljoenen opgeslagen objecten. De locatie van elk object is een hash-tekenreeks. We zijn van plan een opzoeksysteem te ontwikkelen om de locatie van het object te achterhalen op basis van zijn ID. De gebruikelijke manier om dit in Redis te doen, is door een string te gebruiken.
set object:14590860 "007f80f0a62408..."
set object:11678 "009f80abcd0a60..."
...
Deze aanpak werkt prima. Maar aangezien het aantal objecten dat we hebben enorm is, zullen we uiteindelijk veel geheugen nodig hebben voor Redis. We willen het beter doen. Laten we de voor geheugen geoptimaliseerde hash-benadering van dit probleem nemen. We zullen de juiste waarden moeten kiezen voor list-max-ziplist-entries en list-max-ziplist-value . De juiste waarde voor list-max-ziplist-value is wat de maximale lengte van de hash-string van het opslagadres kan zijn. De waarde van list-max-ziplist-entries moet laag genoeg worden gehouden en hangt af van het totale aantal hash-buckets dat we willen maken. Het kan het beste empirisch worden vastgesteld. Voor bijv. voor 100 miljoen objecten konden we ervoor kiezen om 100k hashes te gebruiken. De maximale invoer is in dat geval 100m / 100k =1000. De logica van de toepassing om te beslissen in welke hash het opslagadres van een object gaat, kan zijn:deel de object-ID door 100k en gooi de rest weg. Dus object-ID 14590860 gaat in hash (14590860/100k) =145, d.w.z.
hset object:145 14590860 "007f80f0a62408..."
hget object:145 14590860
> "007f80f0a62408..."
Deze implementatie zal niet alleen veel lichter zijn op het geheugen, maar zou ook een goede cachelocatie moeten bieden.
Dit zijn onze andere posts in de Redis-gegevensstructuurreeks.
- Redis-sets
- Redis-bitmaps
- Gesorteerde sets opnieuw