天天看点

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