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中二级属性进行设置,但是可以在逻辑层面进行删除和管理。