本篇介绍的几种数据结构,虽然没有String使用的那么广泛,但他们有各自适用的场景。
数据结构
1、Hash
用于存储对象。
场景举例
购物车,常用操作包括:
1)往用户的购物车中添加商品
2)增加某人购物车某件商品的数量
3)删除购物车中指定商品
4)获取购物车中商品总数
5)获取购物车中所有商品
对应命令如下:
1)hset car-user1 product1 1
hset key filed value
其中key:car-user1,用来存储user1的购物车信息。
2)hincrby car-user1 product1 2
3)hdel car-user1 product
删除key中的某个filed
4)hlen car-user1
5)hgetall cart-user1
与String比较
针对需要频繁修改存储的对象的某个属性字段的场景下,用hash更方便。
举例说明,需修改用户的余额的字段。若使用string方式存储,需要先将value取出,反序列化成对象,修改,再序列化存储。但使用hash,一个命令就可以搞定了。
2、List
Redis的list结构提供了在列表头/尾插入、取出的操作,因此可以用来实现栈、队列、阻塞队列等数据结构。
其中要实现阻塞队列,需要使用lpush+brpop命令。brpop是阻塞式取出元素。
场景举例
微信和微博的消息流
常用操作:
1)给指定的用户发送消息
2)消息倒序排列,取最新的几条展示
对应命令如下:
1)
lpush user1 a1
lpush user1 a2
lpush user1 a3
2) lrange user1 0 3
lrange key start stop
返回的结果是按插入时间倒序展示
3、Set
提供了元素的集合操作,比如交集、差集等。
场景举例
微博的关注模型
常用操作:
1)(登录用户和当前博主的)共同关注人数
2)我关注的人也关注她
3)可能认识的人
对应命令如下:
我关注的人集合mySet:{‘a’,‘b’}
他关注的人集合hisSet:{‘a’,‘c’,‘e’}
关注他的人集合followerSet:{‘b’,‘e’}
1)sinter myFocus mySet {‘a’}
2)
sismember followerSet a 0
sismember followerSet b 1
sismember key member: 用来查询member元素是否在key集合中,若在,返回1,否则返回0。
3)sdiff hisSet mySet {‘c’,‘e’}
这里,用差集处理。依据就是“他关注的但我没关注的人可能是我认识的人”。
4、ZSet
有序集合
场景举例
微博的热搜榜单
常用操作:
1)当天最热的十条热搜
2)某条热搜的浏览量+1
3)七日搜索榜单计算
4)七日排行前十
对应命令如下:
前提假设,热搜的排行影响因素只有浏览量,浏览量越高越靠前。
1)
添加元素:
zadd n:20210101 1 top1
zadd n:20210101 3 top2
zadd n:20210101 13 top3
查询前十:
zrange n:20210101 0 10 withscores
按浏览量倒序排列取前十
2)zincrby n:20210101 1 top2
执行后查询:
3)新建zset集合20210102
zunionstore: 求多个有序集合的并集
4)重复步骤3操作,然后使用zrevrange查询前十即可。
5、String
最常用的一种类型,简单动态字符串。如果不考虑性能的话,可将任何类型数据序列化后存储进String中。
底层实现
Redis对外提供的这几种数据结构底层都是如何实现的呢?
由上图可以看出:
Redis的基本数据类型,底层都不只有一种数据结构来实现。
以zset为例,就有压缩表和跳跃表两种方式,其中压缩表为默认实现,当数据超过一定范围后改用跳跃表。
有两种情况:
1)zset-max-ziplist-entries
元素超过多少个,会使用跳表。默认为128。
2) zset-max-ziplist-value
单个元素大小超过多少byte,会使用跳表。默认64byte。
小结
Redis的不同的数据类型,是为了不同的应用场景所提供的。我们在了解他们的同时,也要关注每个数据类型的底层数据结构。只有这样,才能更高效的使用他们。
另外,Redis的数据类型操作也在持续不断的扩展中。后续也会有很多更实用更方便的命令出现,我们也要持续关注。这样可以在实际业务场景中提供另一条解决问题的思路。