天天看點

Redis中的Hash類型

Redis-Hash

續上一篇Redis-String,本篇文章為了複習和鞏固而記錄,這次是到Redis的

Hash

類型了

  • 結構
  • 内部編碼
  • 指令和栗子
  • String與Hash對比

結構

key fieId value
user:1:info name aquan
age 23
date 905
viewCounter 658

PS:可以看成一個

key

對應這一個對象裡面有很多屬性,

fieId

是不可以相同的。

内部編碼

Hash類型的内部編碼有兩種,

ziplist

壓縮清單,

hashtable

哈希表。

  • ziplist:當哈希類型元素個數小于

    hash-max-ziplist-entries

    配置(預設512個),同時所有值都小于

    hash-max-ziplist-value

    配置(預設64個位元組)時,Redis會使用

    ziplist

    作為哈希的内部實作

    ziplist

    使用更加緊湊的結構實作多個元素的連續存儲,是以在節省記憶體方面比

    hashtable

    更加優秀。
  • hashtable:當哈希類型無法滿足

    ziplist

    的條件時,Redis會使用

    hashtable

    作為哈希的内部實作。因為此時

    ziplist

    的讀寫效率會下降,而

    hashtable

    的讀寫時間複雜度為O(1)。

我們利用指令進行一下操作

127.0.0.1:6379> hgetall user:1:info
1) "age"
2) "27"
3) "pageview"
4) "2.5"
5) "name"
6) "aquan"
7) "height"
8) "180"
127.0.0.1:6379> object encoding user:1:info
"ziplist"
127.0.0.1:6379> hset user:1:info signature "尋尋覓覓,冷冷清清,凄凄慘慘戚戚。乍暖還寒時候,最難将息。三杯兩盞淡酒,怎敵他、晚來風急!雁過也,正傷心,卻是舊時相識。"
127.0.0.1:6379> hgetall user:1:info
 1) "height"
 2) "180"
 3) "age"
 4) "27"
 5) "name"
 6) "aquan"
 7) "pageview"
 8) "2.5"
 9) "signature"
10) "\xd1\xb0\xd1\xb0\xc3\xd9\xc3\xd9\xa3\xac\xc0\xe4\xc0\xe4\xc7\xe5\xc7\xe5\xa
3\xac\xc6\xe0\xc6\xe0\xb2\xd2\xb2\xd2\xc6\xdd\xc6\xdd\xa1\xa3\xd5\xa7\xc5\xaf\xb
b\xb9\xba\xae\xca\xb1\xba\xf2\xa3\xac\xd7\xee\xc4\xd1\xbd\xab\xcf\xa2\xa1\xa3\xc
8\xfd\xb1\xad\xc1\xbd\xd5\xb5\xb5\xad\xbe\xc6\xa3\xac\xd4\xf5\xb5\xd0\xcb\xfb\xa
1\xa2\xcd\xed\xc0\xb4\xb7\xe7\xbc\xb1\xa3\xa1\xd1\xe3\xb9\xfd\xd2\xb2\xa3\xac\xd
5\xfd\xc9\xcb\xd0\xc4\xa3\xac\xc8\xb4\xca\xc7\xbe\xc9\xca\xb1\xcf\xe0\xca\xb6\xa
1\xa3"
127.0.0.1:6379> object encoding user:1:info
"hashtable"           

複制

指令和栗子

「hget,hset,hdel」

指令 解釋 時間複雜度
hget key fieId 擷取hash key對應的fieId的value O(1)
hset key fieId value 設定hash key對應的fieId的value O(1)
hdel key field 删除hash key對應的fieId的value O(1)
127.0.0.1:6379> hset user:1:info name aquan
(integer) 0
127.0.0.1:6379> hget user:1:info name
"aquan"
127.0.0.1:6379> hdel user:1:info name
(integer) 1
127.0.0.1:6379> hget user:1:info name
(nil)           

複制

「hexists,hlen」

指令 解釋 時間複雜度
hexists key fieId 判斷hash key是否存在fieId O(1)
hlenkey fieId value 擷取hash key fieId的數量 O(1)
127.0.0.1:6379> hgetall user:1:info
1) "age"
2) "27"
3) "pageview"
4) "3"
127.0.0.1:6379> hexists user:1:info age
(integer) 1
127.0.0.1:6379> hexists user:1:info name
(integer) 0
127.0.0.1:6379> hlen user:1:info
(integer) 2           

複制

「hmget,hmset」

指令 解釋 時間複雜度
hmget key fieId1 fieId2 .. fieIdN 批量擷取hash key的一批fieId對應的值 O(n)
hmset key fieId1 value1 fieId2 value2 .. 批量設定hash key的一批fieId value O(n)
127.0.0.1:6379> hgetall user:1:info
1) "age"
2) "27"
3) "pageview"
4) "3"
127.0.0.1:6379> hmget user:1:info age pageview
1) "27"
2) "3"
127.0.0.1:6379> hmset user:1:info name "aquan" height 180
OK
127.0.0.1:6379> hgetall user:1:info
1) "age"
2) "27"
3) "pageview"
4) "3"
5) "name"
6) "aquan"
7) "height"
8) "180"           

複制

「hgetall,hvals,hkeys」

指令 解釋 時間複雜度
hgetall key 擷取hash key中所有的fieId和value O(n)
hvals key 傳回hash key對應所有fieId的value(不傳回fieId) O(n)
hkeys key 傳回hash key對應所有的fieId O(n)

PS:

hgetall

謹慎使用線上上環境,謹記Redis是單線程,要是線上環境hash key裡的

fieId

屬性值多的話,

hgetall

指令傳回時間就會長,後面的指令就會進行排隊等待,建議使用

hmget

指令代替

hgetall

擷取需要的

fieId

127.0.0.1:6379> hgetall user:1:info
1) "age"
2) "27"
3) "pageview"
4) "3"
5) "name"
6) "aquan"
7) "height"
8) "180"
127.0.0.1:6379> hvals user:1:info
1) "27"
2) "3"
3) "aquan"
4) "180"
127.0.0.1:6379> hkeys user:1:info
1) "age"
2) "pageview"
3) "name"
4) "height"           

複制

「hsetnx,hincrby,hincrbyfloat」

指令 解釋 時間複雜度
hsetnx key fieId value 設定hash key對應fieId的value(如fieId已經存在了,則失敗) O(1)
hincrby key fieId num hash key對應的fieId的value自增num(num可以為正負整數) O(1)
hincrbyfloat key fieId num hincrby的浮點數版本 O(1)

PS:

fieId

不可重複,

fieId

對應的

value

可以重複。

127.0.0.1:6379> hgetall user:1:info
1) "age"
2) "27"
3) "pageview"
4) "3"
5) "name"
6) "aquan"
7) "height"
8) "180"
127.0.0.1:6379> hsetnx user:1:info name "haha"
(integer) 0
127.0.0.1:6379> hgetall user:1:info
1) "age"
2) "27"
3) "pageview"
4) "3"
5) "name"
6) "aquan"
7) "height"
8) "180"
127.0.0.1:6379> hincrby user:1:info pageview 2
(integer) 5
127.0.0.1:6379> hincrby user:1:info pageview -1
(integer) 4
127.0.0.1:6379> hincrbyfloat user:1:info pageview -1.5
"2.5"
127.0.0.1:6379> hgetall user:1:info
1) "age"
2) "27"
3) "pageview"
4) "2.5"
5) "name"
6) "aquan"
7) "height"
8) "180"           

複制

String與Hash對比

「相似的API」

String Hash
get hget
set,setnx hset,hsetnx
del hdel
incr,incrby,decr,decrby hincrby
mset hmset
mget hmget

「存儲使用者資訊場景對比」

  • v1:使用String實作
key value(serializable:Json,xml,protobuf)
user:1 {"id":1,"name":"aquan","age":23,"pageView":905}

存儲結構如上表,這樣做是一個

key

對應整個使用者資訊序列化後的

JSON

xml

等,這樣更新資料,存儲資料都要進行序列化操作,更新一個小的屬性的情況,例如更新

age

更新到24,那就先要把這個

age

屬性更新到24,整個對象序列化後,再整個更新到整個

user:1

對應的

key

中的

value

中。

  • v2:使用String實作
key value
user:1:name aquan
user:1:age 23
user:1:pageView 905

存儲結構如上表,可以看到對比v1方法是把資訊拆分為3個

key

單獨存儲使用者對應的資訊,這樣做的好處可以部分更新,不用整個覆寫更新,直覺,添加新的屬性也友善,不影響之前的

key

,這樣做後使用者的資訊不是一個整體,分散到個個

key

中,不便于管理。

  • v3:使用Hash實作
key fieId value
user:1:info name aquan
age 23
viewCounter 905

存儲結構如上表,這樣做可以實作使用

fieId

進行單獨的屬性更新,删除與新增,也讓使用者資訊成為整體友善管理

v1,v2,v3優缺點分析

方案 優點 缺點
v1 程式設計簡單,可能節約記憶體 序列化開銷,設定屬性要操作整個資料
v2 直覺,可以部分更新 記憶體占用較大,key較分散
v3 直覺,節省空間,可以部分更新 程式設計稍微複雜,ttl過期時間不好可控制

PS:過期時間隻能針對key設定,無法針對key中二級屬性進行設定,但是可以在邏輯層面進行删除和管理。