Redis 优化 合理的使用结构来做统计

585 阅读3分钟

业务场景:

  • 手机App中的每天用户登录信息 一天对应一系列用户ID或移动设备ID (聚合)
  • 电商的用户评论列表 一个商品对应一系列的评论 (排序)
  • 用户的签到打卡信息 一天对应一系列的用户签到记录
  • 应用网站的访问信息 一个网页对应一系列的访问点击

聚合统计场景

包括统计多个集合共有的元素、统计其中一个集合独有元素、统计多个集合的所有元素

比如手机App每天新增用户数和第二天的留存用户数

方案一:Set类型 key设计为user:id value记录用户ID

可以发现这样的设计没有日期信息不能以天为维度来统计新增用户 于是我们还得设计 key user:id:${timestamp} value 用户ID

于是所有的用户注册Set就是user:id,某一日期用户注册Set就是user:id:${timestamp}

但是值得注意的是聚合统计 差集、并集、交集计算的复杂度较高,在数据量较大的情况下容易造成Redis实例阻塞。小建议:可以从主从集群中选择一个从库,让他专门负责聚合计算,或把数据读取到客户端,在客户端完成聚合。

排序统计

List按照元素进入List的顺序进行排序,Sorted Set可以根据元素权重来排序。最新评论列表包含所有评论中的最新留言。

可以按照评论时间保存这些评论,每来一个新评论就用LPUSH命令插入 但是一旦分页List就会出现问题,因为List按照插入顺序排序分页过程中如果新插入元素,会造成错位。

Sorted Set 则更适合这个场景。

ZADD comments ${no} content

ZRANGEBYSCORE comments no9{no-9} {no} 最新10条评论

二值状态统计

签到未签到、已读未读都是非常典型的二值状态。这时我们可以使用Bitmap来进行保存。Bitmap本身是用String类型作为底层数据结构实现的。

例如我们要统计ID 3000的用户在2020年8月的签到情况

可以使用 setbit uid:sign:3000:202008 2 1 表示用户8月3日已签到 统计用户8月份签到次数 bitcount uid:sign:3000:202008

基数统计

统计一个集合不重复元素的个数。比如UV

  • Set方案:

用一个用户访问了page1,这个信息加到Set中

SADD page1:uv user1

SCARD page1:uv 就可以计算出集合的元素个数

  • Hash方案:

HSET page1:uv user1 1 表示用户1访问过1次page1页面

  • HyperLogLog方案:

PFADD page1:uv user1 user2 user3 user4 PFCOUNT page1:uv

优点元素个数非常多时,计算基数所需要的空间总是固定 12KB 这里利用了稀疏图和稠密图 缺点统计规则基于概率完成 又标准误算率