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:當哈希類型元素個數小于
配置(預設512個),同時所有值都小于hash-max-ziplist-entries
配置(預設64個位元組)時,Redis會使用hash-max-ziplist-value
作為哈希的内部實作ziplist
使用更加緊湊的結構實作多個元素的連續存儲,是以在節省記憶體方面比ziplist
更加優秀。hashtable
- hashtable:當哈希類型無法滿足
的條件時,Redis會使用ziplist
作為哈希的内部實作。因為此時hashtable
的讀寫效率會下降,而ziplist
的讀寫時間複雜度為O(1)。hashtable
我們利用指令進行一下操作
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中二級屬性進行設定,但是可以在邏輯層面進行删除和管理。