sql >> Databasteknik >  >> NoSQL >> Redis

Introduktion till Redis Data Structures:Hashes

Redis-haschar är (intuitivt nog!) hash som mappar strängnamn till strängvärden. De kallas i huvudsak behållare med unika fält och deras värden. De är det perfekta sättet att representera ett objekt som en Redis-datastruktur. Som förväntat tillhandahåller de grundläggande operationer med konstant tid som get, set, exists etc. Ett gäng avancerade operationer tillhandahålls också. Den fullständiga listan över hash-kommandon finns här.

Låt oss ta det en sväng från 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"

Som vi har börjat förvänta oss av vår hosting för Redis™* som en datastrukturserver ser vi att Redis tillhandahåller ganska användbara och avancerade operationer på hash.

Internt

Precis som Redis Sets implementeras även Redis-haschar som ordböcker. Ordböcker i Redis är implementerade som hashtabeller som använder hashfunktionen MurmurHash2 och växer via inkrementell storleksändring. Hashkollisioner hanteras genom kedja. Mer information finns i Redis-implementeringen av ordboken på dict.c.
Som med Sets finns det lagringsoptimering gjord för mindre hash. Denna datastruktur kallas ziplist (Hashes optimerades med en annan datastruktur som kallas zipmap före Redis 2.6) i Redis-implementeringen. Det är i huvudsak en speciellt kodad dubbellänkad lista som är optimerad för minnesbesparingar. Datan, såväl som pekarna, lagras inline. Ziplist används också för att optimera lagring av mindre sorterade set och listor. En hash när den plattas till en sådan lista ser ut ungefär som [nyckel1, värde1, nyckel2, värde2, ...]. Hur är detta mer effektivt än vanliga nycklar? Hashes med få nycklar kan på ett smart sätt packas in i den här linjära arrayliknande strukturen (d.v.s. ziplistan) samtidigt som de garanterar amorterad O(1)-prestanda för get och set. Uppenbarligen kan detta inte hänga med när hashfälten ökar. När hashen växer omvandlas den till standardordboksstrukturen för att bibehålla O(1)-prestanda och utrymmesbesparingarna går förlorade. Redis-konfigurationsparametrar som styr denna transformation är:

  • list-max-ziplist-entries default (512):Ändra till standardrepresentation om hash växer sig större än denna gräns.
  • list-max-ziplist-value default (64):Ändra till standardrepresentation om det största elementet i hashen blir större än denna gräns.

Mer detaljer kan förstås från koden och kommentarerna i implementeringen som finns här. Minnesbesparingarna genom att använda denna speciella optimering är betydande. Vi kommer att prata mer om i nästa avsnitt.

Minnesoptimering

En av de välkända rekommendationerna för minnesbesparingar när du använder Redis är att använda hash istället för vanliga strängar. Detta är ett viktigt användningsfall för att utnyttja kraften i Redis-haschar i verkliga applikationer. Från den officiella Redis-dokumentationen om minnesoptimering:

Använd hash när det är möjligt

Små hashar kodas i ett mycket litet utrymme, så du bör försöka representera din data med hash varje gång det är möjligt. Om du till exempel har objekt som representerar användare i en webbapplikation, istället för att använda olika nycklar för namn, efternamn, e-post, lösenord, använd en enda hash med alla obligatoriska fält.

Det här inlägget fortsätter sedan med att föreslå ett sätt att mappa en rad objekt till en uppsättning hash för att dra fördel av minnesbesparingarna. Instagram, i ett mycket populärt blogginlägg, beskriver hur de använder en liknande teknik som hjälpte dem att uppnå storleksordningar på potentiella besparingar. En annan blogg som försöker mäta fördelarna med optimeringen är denna.

Applikationer

  • Redis Hashes är naturligt lämpade för att lagra objekt:sessioner, användare, besökare etc. Detta gör det till en av nyckeldatastrukturerna som tillhandahålls av Redis.
  • I sin minnesoptimerade form är den ett utmärkt val för att cachelagra stora mängder data.

Ett objektadressarkiv

Eftersom minnesoptimering är ett viktigt användningsfall för hash, låt oss diskutera ett exempel som liknar Instagram-implementeringen för att visa hur man använder minnessparande funktioner i Redis hash. Låt oss säga att vi har ett enormt innehållsadresserbart lagringsutrymme (CAS) med hundratals miljoner objekt lagrade. Varje objekts plats är en hashsträng. Vi avser att utveckla ett uppslagssystem för att ta reda på objektets plats givet dess ID. Det typiska sättet att göra detta i Redis är att använda en sträng.

set object:14590860 "007f80f0a62408..."
set object:11678 "009f80abcd0a60..."
...

Det här tillvägagångssättet fungerar utmärkt. Men eftersom antalet objekt vi har är enormt, kommer vi att behöva mycket minne för Redis. Vi vill göra det bättre. Låt oss ta den minnesoptimerade hash-metoden för detta problem. Vi måste välja rätt värden för list-max-ziplist-poster och list-max-ziplist-value . Rätt värde för list-max-ziplist-value är vad maxlängden på hashsträngen för lagringsadressen kan vara. Värdet på list-max-ziplist-poster måste hållas tillräckligt lågt och beror på antalet totala hash-buckets vi vill skapa. Det kommer bäst att redas ut empiriskt. För t.ex. för 100 miljoner objekt kunde vi välja att använda 100 000 hash. De maximala posterna i det fallet kommer att vara 100m / 100k =1000. Applikationens logik för att bestämma vilken hash ett objekts lagringsadress går in i kan vara:dividera objekt-ID med 100k och kassera resten. Således kommer objekt-ID 14590860 att gå in i hash (14590860/100k) =145 dvs.


hset object:145 14590860 "007f80f0a62408..."
hget object:145 14590860
> "007f80f0a62408..."

Denna implementering kommer inte bara att vara mycket lättare för minnet utan bör också ge bra cache-lokalitet.

Här är våra andra inlägg i Redis datastrukturserie.

  • Redis-set
  • Redis bitmappar
  • Återupprätta sorterade uppsättningar

  1. Python-redis keys() returnerar en lista med byteobjekt istället för strängar

  2. Hantera långvarig verksamhet i MongoDB

  3. Hur man tar bort ett element från en dubbelkapslad array i ett MongoDB-dokument.

  4. Hämta BinData UUID från Mongo som sträng