之前对Redis写了很多文章了。其中Redis(二)介绍了数据类型和操作。
这篇文章作为补充,并且侧重于是介绍一下每一种类型的使用场景
具体操作这里就不一一列举了。可以查看或之前的博客或者去菜鸟网
规范
实际中如何给Key命名?
- 以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id
- 控制key的长度不能太长
- 不要包含特殊字符:换行、双引号、空格等
Value不能太大
非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,Redis4.0支持了异步del)
举例(这是hash类型):
1 | hmset user:1 name tom age 19 favor football |
尽量均匀的设置过期时间
更多参考此博客:https://zhuanlan.zhihu.com/p/92633604
strings
结构:简单的Key-Value形式,Value是一个字符串
场景:
String是最基本、常用的数据类型
- 可以用来存储普通的、JSON等形式的字符串数据
- 假如数据正好是一个整数,那么你可以使用
incr
命令让他进行原子自增,或decr
原子自减【注意小数是不行的】
hash
结构:Value是一个类似Map的结构
容量:Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)
场景:hash类型非常适合用来存储对象类的数据,假如在String里存储一个JSON格式的字符串来保存对象,在存入、取出时还需要来回转换。而hash更加直接快速,还可以动态的添加或删除”字段名”,更加灵活
list
结构:
Redis中list的实现是一个双向链表,并不是数据。lpush rpush
分别对应头插入和尾部插入。
同样我们也可以选择lpop rpop
来进行头部移出,和尾部移出。这样list的也就相当于队列或者栈了
容量:一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)
场景:
- 消息队列
list类型的lpop和rpush(或者反过来,lpush和rpop)能实现队列的功能,故而可以用Redis的list类型实现简单的点对点的消息队列。不过我不推荐在实战中这么使用,因为现在已经有Kafka、NSQ、RabbitMQ等成熟的消息队列了,它们的功能已经很完善了,除非是为了更深入地理解消息队列,不然我觉得没必要去重复造轮子。
2. 排行榜
list类型的lrange命令可以分页查看队列中的数据。可将每隔一段时间计算一次的排行榜存储在list类型中,如京东每日的手机销量排行、学校每次月考学生的成绩排名、斗鱼主播排名等
- 最新列表
list类型的lpush命令和lrange命令能实现最新列表的功能,每次通过lpush命令往列表里插入新的元素,然后通过lrange命令读取最新的元素列表,如朋友圈的点赞列表、评论列表。
但是,并不是所有的最新列表都能用list类型实现,因为对于频繁更新的列表,list类型的分页可能导致列表元素重复或漏掉,举个例子,当前列表里由表头到表尾依次有(E,D,C,B,A)五个元素,每页获取3个元素,用户第一次获取到(E,D,C)三个元素,然后表头新增了一个元素F,列表变成了(F,E,D,C,B,A),此时用户取第二页拿到(C,B,A),元素C重复了。只有不需要分页(比如每次都只取列表的前5个元素)或者更新频率低(比如每天凌晨更新一次)的列表才适合用list类型实现。对于需要分页并且会频繁更新的列表,需用使用有序集合sorted set类型实现。另外,需要通过时间范围查找的最新列表,list类型也实现不了,也需要通过有序集合sorted set类型实现,如以成交时间范围作为条件来查询的订单列表。
为何不直接使用sorted set?
对于排行榜和最新列表两种应用场景,list类型能做到的sorted set类型都能做到,list类型做不到的sorted set类型也能做到,那为什么还要使用list类型去实现排行榜或最新列表呢,直接用sorted set类型不是更好吗?原因是sorted set类型占用的内存容量是list类型的数倍之多,对于列表数量不多的情况,可以用sorted set类型来实现,比如上文中举例的打擂金曲排行榜,每天全国只有一份,两种数据类型的内存容量差距可以忽略不计,但是如果要实现某首歌曲的翻唱作品地区排行榜,数百万的歌曲,300多个地区,会产生数量庞大的榜单,或者数量更加庞大的朋友圈点赞列表,就需要慎重地考虑容量的问题了
set
结构:
- 是一个无序、不重复集合。若添加了一个重复的元素,则反回0(失败)
- 提供了多个set之间的聚合运算:如何求交集、并集、差集等
- 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
容量:集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)
使用场景:
- 好友/关注/粉丝/感兴趣的人集合
set类型唯一的特点使得其适合用于存储好友/关注/粉丝/感兴趣的人集合,集合中的元素数量可能很多,每次全部取出来成本不小,set类型提供了一些很实用的命令用于直接操作这些集合,如
a. sinter命令可以获得A和B两个用户的共同好友
b. sismember命令可以判断A是否是B的好友
c. scard命令可以获取好友数量
d. 关注时,smove命令可以将B从A的粉丝集合转移到A的好友集合
f. sdiff 返回第一个集合与其他集合之间的差异。
1 | #将ABCD存入集合 set1 中 |
需要注意的是,如果你用的是Redis Cluster集群,对于sinter、smove这种操作多个key的命令,要求这两个key必须存储在同一个slot(槽位)中,否则会报出 (error) CROSSSLOT Keys in request don’t hash to the same slot 错误。Redis Cluster一共有16384个slot,每个key都是通过哈希算法CRC16(key)获取数值哈希,再模16384来定位slot的。要使得两个key处于同一slot,除了两个key一模一样,还有没有别的方法呢?答案是肯定的,Redis提供了一种Hash Tag的功能,在key中使用{}括起key中的一部分,在进行 CRC16(key) mod 16384 的过程中,只会对{}内的字符串计算,例如friend_set:{123456}和fans_set:{123456},分别表示用户123456的好友集合和粉丝集合,在定位slot时,只对{}内的123456进行计算,所以这两个集合肯定是在同一个slot内的,当用户123456关注某个粉丝时,就可以通过smove命令将这个粉丝从用户123456的粉丝集合移动到好友集合。相比于通过srem命令先将这个粉丝从粉丝集合中删除,再通过sadd命令将这个粉丝加到好友集合,smove命令的优势是它是原子性的,不会出现这个粉丝从粉丝集合中被删除,却没有加到好友集合的情况。然而,对于通过sinter获取共同好友而言,Hash Tag则无能为力,例如,要用sinter去获取用户123456和456789两个用户的共同好友,除非我们将key定义为{friend_set}:123456和{friend_set}:456789,否则不能保证两个key会处于同一个slot,但是如果真这样做的话,所有用户的好友集合都会堆积在同一个slot中,数据分布会严重不均匀,不可取,所以,****在实战中**使用Redis Cluster时,sinter这个命令其实是不适合作用于两个不同用户对应的集合的**(同理其它操作多个key的命令)。
2. 随机展示
通常,app首页的展示区域有限,但是又不能总是展示固定的内容,一种做法是先确定一批需要展示的内容,再从中随机获取。如下图所示,酷狗音乐K歌擂台赛当日的打擂歌曲共29首,首页随机展示5首;昨日打擂金曲共200首,首页随机展示30首。
set类型适合存放所有需要展示的内容,而srandmember
命令则可以从中随机获取几个。
3. 黑名单/白名单
经常有业务出于安全性方面的考虑,需要设置用户黑名单、ip黑名单、设备黑名单等,set类型适合存储这些黑名单数据,sismember
命令可用于判断用户、ip、设备是否处于黑名单之中。
sorted set
与set一样是一个不重复的集合。区别是集合中每一个成员都会有一个分数。
- 不同的是每个元素都会关联一个 double 类型的分数
- redis 正是通过分数来为sorted set集合中的成员进行从小到大的排序。
- 有序集合的成员是唯一的,但分数(score)却可以重复
1 | #向zset1集合中 存入三个成员 |
使用场景:
带分数的排名:比如商品销量排名,游戏玩家积分排名等等
bitmap
位图:当做是一个bit数组,我们向每一位来set设置0或1,也可以get
场景:
- 设置用户在线状态,所有用户用一个key,用户id作为offset
- 用户签到情况,每一个用户作为一个bitmap的key
hyperLoglog
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
什么是基数?
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
geo
GEO即地址信息定位
可以用来存储经纬度,计算两地距离,范围计算等
参考资料:
- Redis应用类型及应用场景 https://segmentfault.com/a/1190000012212663?utm_source=tag-newest
- Redis实战经验 https://www.cnblogs.com/pangzizhe/p/10674501.html
- https://www.cnblogs.com/renpingsheng/p/9783834.html