十一:大数据量集合如何进行keys统计?

 有以下四种常用的集合统计模式,我们该怎么选择统计模式,才能更快、更节省内存地进行统计?

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

1. 聚合统计

聚合统计就是统计多个集合元素的聚合结果,包括:统计多个集合的共有元素(交集统计)、两个集合相比,其中一个集合的特有元素(差集统计)、多个集合的所有元素(并集统计)。

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

实现:

累计用户Set: key为user : id,value为一个Set集合,记录所有登录过的用户ID

每日用户Set: key为user : id : 当天日期,value是一个Set集合,记录当天登录的用户ID

统计每天新增用户ID时:

SUNIONSTORE  user:id  user:id  user:id:20220511     //把2022年5月11日的登录用户集合ID先存到累计用户ID集合中
SDIFFSTORE  user:new  user:id:20220512  user:id		//把2022年5月12日的登录用户集合ID和累计用户ID集合做差集

此时,得到的user:new集合存储的就是12号的新增用户ID;

而求留存用户数也是类似,只不过时求连续两天的登录集合的交集即可。

但是,如果数据量比较大,直接执行这些计算,会导致Redis阻塞 => 从主从集合中选择一个从库,让它专门负责聚合计算,或者把数据读取到客户端,让客户端来统计。

2. 排序统计

如果需要对集合元素进行排序,那么就需要用到排序统计模式。Redis的数据类型中,List和Sorted Set都是有序集合,那么这两个有什么不同呢? => **List是按照元素进入List的顺序进行排序的,而Sorted Set是根据元素的权重值来排序的,**我们可以自己决定每个元素的权重值,比如根据插入集合的时间来确定权重,先插入的权重小,后插入的权重大。 那么我们要怎么来选择呢?

典型场景:显示最新评论列表。

List:每个商品对应一个List,包含了这个商品的全部评论信息,按时间排序,每来一个评论,就会LPUSH到列表的队头,在只有一页评论的时候,没什么问题,但是评论往往有很多页,在进行分页显示的时候,List就会出现问题,比如查看第一页LRANGE product 0 2,显示“A” “B” "C"三条数据,然后准备查看第二页LRANGE product 3 5,应该显示“D” “E” “F”三条数据,但是在查看第二页之前,又来了一条最新评论,插在了第一页的最前面,那么此时查看第二页数据,显示的是“C” “D” “E”,“C”数据又出现了一次。这是因为List是通过元素在List中的位置排序的,当有一个新元素插入时,原先的所有元素全部后移了一位,这就导致,在使用LRANGE读取时,会读到旧元素。

Sorted Set:不会出现List的问题,因为它是根据元素的实际权重值来排序和获取数据的,只要我们给每个元素设置好权重值,然后在获取元素的时候也通过权重值来获取,就不会出现数据变动的问题。假设越新的评论权重值越大,目前最新评论的权重是N,我们执行命令ZRANGEBYSCORE comments N-9 N 就可以获取到最新的10条评论。

小结:在展示最新列表、排行榜等场景,或者数据更新频繁或者需要分页显示时,优先考虑Sorted Set。

3. 二值状态统计

二值状态:集合元素的取值只有0和1两种,比如签到,只用记录签到1和未签到0。 => BitMap数据类型。

**BitMap数据类型:**可以看作一个bit数组,底层是String类型。提供了SETBIT/GETBIT/BITCOUNT命令,可以对一个位置offset的bit位进行读写、对整个数组上的所有1进行统计。提供了BITOP命令,可以对多个BitMap按位与、或、异或,并保存到新的BitMap中。

典型场景1:统计ID 3000的用户在2022年5月份的签到情况。

SETBIT  uid:sign:3000:202205 12 1    //记录该用户2022年5月13号签到
GETBIT  uid:sign:3000:202205 12 	 //检查该用户2022年5月13号是否签到
BITCOUNT  uid:sign:3000:202205		 //统计该用户在2022年5月份签到情况

典型场景2:统计1亿个用户在10天内连续签到的用户数。

以日期为key,每个key对应一个1亿位的BitMap,每一个bit对应一个用户在当天的签到情况,即有10个1亿位的BitMap;对10个BitMap做”与“操作,得到一个新的BitMap,这个BitMap中,只有10天连续签到的用户bit位上才是1;用BITCOUNT统计这个新BitMap中1的个数,即为连续10天签到的用户数。

开销情况:1个1亿位的BitMap,大约占12MB的内存(10^8/8/1024/1024),10个就是120MB,内存压力不算大,在实际使用的时候最好设置过期时间,使其自动删除。

小结:如果需要统计的数据只有两种状态,就可以使用BitMap,能够极大节省空间。

4. 基数统计

基数统计:统计一个集合中不重复的元素的个数。

典型场景:统计网页的UV(Unique Vistor,独立访客数,一个用户一天内的多次访问只能算作一次,需要去重)。

实现:

  • Set:默认支持去重,使用SADD page1:uv user1增加用户,使用SCARD获取元素个数,但是,当用户量特别大时,Set会消耗很大的内存空间;
  • Hash:使用HSET page1:uv user 1增加用户,使用HLEN获取元素数,但是同样也非常浪费内存;
  • **HyperLogLog:用于统计基数的数据集合类型,而且当集合元素数量特别多时,它计算基数所需的空间是固定的,而且还很小。**每个HyperLogLog只需要12KB的内存就可以计算接近2^64个元素的基数。极大地节省了内存空间。PFADD page1:uv user1 user2 user3添加用户,PFCOUNT page1:uv 获取不重复元素个数。

注意:HyperLogLog的统计是基于概率实现的,也就是说有一定的误差,误差率大概在0.81%。如果需要精确统计的话,最好还是用Set或Hash。

redis 统计 活跃用户数 redis 汇总计算_nosql