Redis补充(数据类型以及使用场景)
2022-09-22 22:46:53

之前对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亿个元素)

场景:

  1. 消息队列

list类型的lpop和rpush(或者反过来,lpush和rpop)能实现队列的功能,故而可以用Redis的list类型实现简单的点对点的消息队列。不过我不推荐在实战中这么使用,因为现在已经有Kafka、NSQ、RabbitMQ等成熟的消息队列了,它们的功能已经很完善了,除非是为了更深入地理解消息队列,不然我觉得没必要去重复造轮子。

 2. 排行榜

list类型的lrange命令可以分页查看队列中的数据。可将每隔一段时间计算一次的排行榜存储在list类型中,如京东每日的手机销量排行、学校每次月考学生的成绩排名、斗鱼主播排名等

  1. 最新列表

  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多亿个成员)

使用场景:

  1. 好友/关注/粉丝/感兴趣的人集合

  set类型唯一的特点使得其适合用于存储好友/关注/粉丝/感兴趣的人集合,集合中的元素数量可能很多,每次全部取出来成本不小,set类型提供了一些很实用的命令用于直接操作这些集合,如

   a. sinter命令可以获得A和B两个用户的共同好友

   b. sismember命令可以判断A是否是B的好友

  c. scard命令可以获取好友数量

  d. 关注时,smove命令可以将B从A的粉丝集合转移到A的好友集合

​ f. sdiff 返回第一个集合与其他集合之间的差异。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#将ABCD存入集合 set1 中
127.0.0.1:6379> sadd set1 A B C D
(integer) 4
#将BD存入集合 set1 中
127.0.0.1:6379> sadd set2 B D
(integer) 2
#求set1 set2交集
127.0.0.1:6379> sinter set1 set2
1) "D"
2) "B"
#---
#Redis Sdiff 命令返回第一个集合与其他集合之间的差异,
#也可以认为说第一个集合中独有的元素。不存在的集合 key 将视为空集
#---
#求set1 与 set2的差集
127.0.0.1:6379> sdiff set1 set2
1) "A"
2) "C"
#求set2 与 set1的差集
127.0.0.1:6379> sdiff set2 set1
(empty list or set)
#判断A 是否在结合set1中
127.0.0.1:6379> sismember set1 A
(integer) 1

  需要注意的是,如果你用的是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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#向zset1集合中 存入三个成员
127.0.0.1:6379> zadd zset1 100 xiaoming 50 red 10.6 green
(integer) 3
#查询0-2索引范围的数据 默认正序:从小到大
127.0.0.1:6379> zrange zset1 0 2
1) "green"
2) "red"
3) "xiaoming"
#查询0-2索引范围的数据,倒叙 revrange-> reverse range
127.0.0.1:6379> zrevrange zset1 0 2
1) "xiaoming"
2) "red"
3) "green"
#查询某个成员的分数
127.0.0.1:6379> zscore zset1 green
"10.6"

使用场景:

带分数的排名:比如商品销量排名,游戏玩家积分排名等等

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即地址信息定位
可以用来存储经纬度,计算两地距离,范围计算等

参考资料:

Prev
2022-09-22 22:46:53
Next