0
点赞
收藏
分享

微信扫一扫

Redis核心技术与实战-学习笔记(十二):一亿个keys要统计,应该用哪种集合


一.Redis的使用场景

一个key对应一个数据集合

  • 登录信息:一天对应一系列用户或移动设备id
  • 电商网站上商品的用户评论列表:一个商品对应了一系列的评论
  • 用户在手机app上的签到打卡信息:一天对应一系列的签到记录
  • 应用网站上网页访问信息:一个网站对应一系列的访问点击

对集合中的数据进行统计

  • 新增用户数和第二天的留存用户数
  • 电商网站的商品评论中需要统计评论列表的最新评论
  • 签到打卡中,需要统计一月中连续打卡的用户数
  • 网页访问记录中,需要统计独立访客(Unique Visitor,UV)量。

二.常用集合的统计模式

聚合统计,排序统计,二值状态统计和基数统计

聚合统计

聚合统计:统计多个集合元素的聚合结果。

交集统计:统计多个集合共有元素。

差集统计:两个集合相比,统计其中一个集合独有的元素。

并集统计:统计多个集合的所有元素。

场景1:统计手机APP每天新增用户和第二天留存用户数

实现方案:

用一个集合记录所有登录过APP的用户ID,同时,用另一个集合记录每一天登录过APP的用户ID,然后对两个集合做聚合统计。

1.记录所有登录过APP的用户ID

user:id,表示记录的是用户id

Set集合,里面是所有登录过APP用户ID

            将这个Set成为累计用户Set

 

Redis核心技术与实战-学习笔记(十二):一亿个keys要统计,应该用哪种集合_perl

 2.每天登录的用户ID(每日用户set):

user:id:20220211

用户id

Redis核心技术与实战-学习笔记(十二):一亿个keys要统计,应该用哪种集合_database_02

统计每天新增用户,计算每日用户set和累计用户set的差集。

实现:如果手机APP在8月3日上线,将当天登录的用户记录到key为user:id:20200803的Set中。

8月3日新增用户是user:id:20200803这个set中用户就是当天新增用户集。

SUNIONSTORE user:id user:id user:id:20200803

计算累计用户 Set 和 user:id:20200803 Set 的并集结果,结果保存在 user:id 这个累计用户 Set 中。

SDIFFSTORE  user:new  user:id:20200804 user:id

  将8月4日的登录用户ID记录到user:id:20200804的set中,求出累计用户 Set 和 user:id:20200804 Set 的差集,就是8月4日的新增用户数量。

计算8月4日留存用户:user:id:20200803和user:id:20200804两个set的交集

SINTERSTORE user:id:rem user:id:20200803 user:id:20200804

set聚合统计的风险

  • 计算复杂度较高,在数据量较大情况下,会导致Redis实例阻塞。

解决方案

从库专门负责聚合计算,或把数据读取到客户端由客户端完成聚合统计,这样可以规避阻塞主库实例和其他从库实例的风险。

排序统计  

      集合元素排序需求方法实现

      电商网站提供最新评论列表的场景

有序集合:集合中的元素可以按序排列。

Redis常用的四种集合类型(List,Hash,set,sorted Set)

List(元素进入顺序排序)和Sorted Sort(根据元素权重排序)属于有序集合。

排列。

List的实现方式

  • 每个商品对应一个List评论集合,新的平均LPUSH命令插入List的队头。在只有一页评论时候,可以很清晰看到最新的评论。
  • 分页实现时候可能会出现问题

List------> A,B,C,D,E,F

 A是最新评论,F是最早评论

展示第一页3个评论时,用下面的命令,得到最新的三条评论A,B,C:


lrange product1 0 2
1."A"
2."B"
3."c"

再用下面的命令获取第二页的 3 个评论,也就是 D、E、F。

lrange product1 3 5
1."D"
2."E"
3."F"

问题

如果展示第二页前,新评论G被lpush命令插入评论list的队头,评论list变成{G,A,B,C,D,E,F},此时用刚才命令获取第二页评论时,会发现数据为

lrange product1 3 5
1."C"
2."D"
3."E"

产生原因:

lrange读取时就会读到旧数据。

(list会使数据下标变化,但是set中数据的score是不变的,分页时记录上一次的范围起始值就可以了 取第2页之前如果有新数据加入,那只会让逻辑上的第一页的数据量增大,第二页起始不变,因为记录了原先第一页末尾的score,是fixed的)

Sorted Set的实现方式

sorted set中。

ZRANGEBYSCORE命令按照权重排序后返回元素,即使集合中的元素频繁更新,Sorted set也能通过ZRANGEBYSCORE命令准确获取按序排列的数据。

       越新的评论权重越大,目前最新评论的权重是N,我们执行命令时候,就可以获得最新的10条评论。 

ZRANGEBYSCORE comments N-9 N

数据频繁更新或者分页显示,优先考虑Sorted Set。

二值状态统计

      场景三:用户在手机app上的签到打卡信息:一天对应一系列的签到记录

二值状态统计(集合元素的取值就只有0和1两种)。

只用记录签到(1)和未签到(0),所以这是非常典型的二值状态。

用1个bit位表示,一个月(31天)只用31个bit位就可以,而一年的签到只需要用到365个bit位。

Bitmap

         Redis提供的扩展数据结构

Bitmap本身用String类型作为底层数据结构实现的一种统计二值状态的数据类型。

二进制的字节数组。

  • 一个元素的二值状态。可以把Bitmap看做是一个bit数组。
  • offset对bit数组的某一个bit位进行读写。
  • Bitmap偏移量从0开始,也就是offset最小值是0。
  • 当使用SETBIT对一个bit位进行读写操作时,这个bit位会被设置为1。
  • Bitmap还提供了BITCOUNT操作,来统计这个bit数组中所有为"1"的个数。

Bitmap实现签到统计

    需求:统计ID 3000用户在2020年8月份的签到情况

存入:

SETBIT uid:sign:3000:202008 2 1

查询:

GETBIT uid:sign:3000:202008 2

统计用户在8月份的签到次数

BITCOUNT uid:sign:3000:202008

记录1亿个用户10天签到情况,统计这10天连续签到的用户总数

技术支持:BITOP

Bitmap支持用BITOP命令对多个Bitmap按位做"与"或"异或"操作,操作结果存入新的Bitmap中。

"与"操作:三个Bitmap bm1,bm2,bm3对bit位做"与"操作,结果保存到一个新的Bitmap中

Redis核心技术与实战-学习笔记(十二):一亿个keys要统计,应该用哪种集合_数据库_03

 解决方案:

每天日期作为key,每个key对应一个1亿位的Bitmap.

每一个bit位对应一个用户当天的签到情况

10天的Bitmap做"与"操作,得到的结果也是一个Bitmap。

BITCONT统计下Bitmap中1的个数,得到连续签到10天的用户总数。

内存开销:

1亿位的Bitmap,占用内存12MB (10^8bit)

120MB

优化:

设置过期时间,让Redis自动删除不需要签到记录,以节省内存开销。

总结:

统计数据的二值状态,例如商品有没有,用户在不在。它只用一个bit位表示0或1。在记录海量数据时,Bitmap能够有效的节省内存空间。

基数统计 

统计一个集合中不重复元素个数。例如:统计网页UV。

网页UV统计有个独特的地方,就是需要去重,一个用户一天内多次访问只能算作一次。

Redis集合类型中,Set类型默认支持去重。

Set实现方式:

      一个用户user1访问page1时:

SADD page1:uv user1

你需要统计 UV 时,可以直接用 SCARD 命令,这个命令会返回一个集合中的元素个数。

Hash实现方式

HSET page1:uv user1 1

当要统计 UV 时,我们可以用 HLEN 命令统计 Hash 集合中的所有元素个数。

缺点:消耗内存

HyperLogLog

当集合元素数量非常多时,他计算基数所需空间总是固定的,而且还很小。

每个HyperLogLog只需要花费12KB内存,可以计算接近2^64个元素的基数。

HyperLogLog 中添加新元素)把访问页面的每个用户都添加到 HyperLogLog 中。

PFADD page1:uv user1 user2 user3 user4 user5

PFCOUNT命令直接获得page1的UV值,这个命令的作用就是返回HyperLogLog的统计结果。

PFCOUNT page1:uv

缺点:HyperLogLog的统计规则是基于概率完成的,误差率是0.81%。使用HyperLogLog统计UV是100万,实际UV可能是101万,如果需要精确统计结果,最好还是继续用Set或Hash类型。

数据类型

聚合统计

排序统计

二值状态统计

基数统计

set

支持交,并,差集计算

不支持

不支持

精确统计大数据量时,效率低内存开销大

sorted set

支持交,并集计算

支持

hash

不支持

不支持

List

不支持

支持

不支持,元素没有去重

Bitmap

与或异或

不支持

支持,大数据量时,效率高省内存

精确统计,大数据量时内存开销大于HyperLogLog

HyperLogLog

不支持

不支持

不支持

概率统计,大数据量时,非常节约内存

举报

相关推荐

0 条评论