• Redis的Set类型、Sorted Set类型、Bitmap类型和HyperLogLog


    1、集合元素统计的第一个场景:聚合统计

    当你需要对多个集合进行聚合计算时,Set 类型会是一个非常不错的选择。

    不过,我要提醒你一下,这里有一个潜在的风险。

    Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞。所以,我给你分享一个小建议:你可以从主从集群中选择一个从库,让它专门负责聚合计算,或者是把数据读取到客户端,在客户端来完成聚合统计,这样就可以规避阻塞主库实例和其他从库实例的风险了。

    1. SetOperations setOperations = redisTemplate.opsForSet();
    2. setOperations.add("user:id","zhangsan1");
    3. setOperations.add("user:id","lisi2");
    4. setOperations.add("user:id","wangwu34");
    5. setOperations.add("user:id","liuliu44");
    6. setOperations.add("user:id","qiqi55");
    7. Set sets= redisTemplate.opsForSet().members("user:id");
    8. sets.stream().forEach(key-> System.out.println(key));

     

    2、排序统计

    在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,建议你优先考虑使用 Sorted Set

    1. ZSetOperations zSetOperations = redisTemplate.opsForZSet();
    2. zSetOperations.add("Goods","nice" , System.currentTimeMillis());
    3. zSetOperations.add("Goods","pretty", System.currentTimeMillis());
    4. zSetOperations.add("Goods","bad", System.currentTimeMillis());
    5. zSetOperations.add("Goods","middle", System.currentTimeMillis());

     

    3、二值状态统计

    现在,我们再来分析下第三个场景:二值状态统计。这里的二值状态就是指集合元素的取值就只有 0 和 1 两种。在签到打卡的场景中,我们只用记录签到(1)或未签到(0),所以它就是非常典型的二值状态,

    在签到统计时,每个用户一天的签到用 1 个 bit 位就能表示,一个月(假设是 31 天)的签到情况用 31 个 bit 位就可以,而一年的签到也只需要用 365 个 bit 位,根本不用太复杂的集合类型。这个时候,我们就可以选择 Bitmap。这是 Redis 提供的扩展数据类型。我来给你解释一下它的实现原理。

    Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。String 类型是会保存为二进制的字节数组,所以,Redis 就把字节数组的每个 bit 位利用起来,用来表示一个元素的二值状态。你可以把 Bitmap 看作是一个 bit 数组。

    我们可以计算一下记录了 10 天签到情况后的内存开销。每天使用 1 个 1 亿位的 Bitmap,大约占 12MB 的内存(10^8/8/1024/1024),10 天的 Bitmap 的内存开销约为 120MB,内存压力不算太大。不过,在实际应用时,最好对 Bitmap 设置过期时间,让 Redis 自动删除不再需要的签到记录,以节省内存开销。

    所以,如果只需要统计数据的二值状态,例如商品有没有、用户在不在等,就可以使用 Bitmap,因为它只用一个 bit 位就能表示 0 或 1。在记录海量数据时,Bitmap 能够有效地节省内存空间。

    1. redisTemplate.opsForValue().setBit("userId:001:07",22,true);
    2. redisTemplate.opsForValue().setBit("userId:001:07",24,true);
    3. boolean flag = redisTemplate.opsForValue().getBit("userId:001:07",24);

    4、基数统计

    我们再来看一个统计场景:基数统计。基数统计就是指统计一个集合中不重复的元素个数。对应到我们刚才介绍的场景中,就是统计网页的 UV。

    网页 UV 的统计有个独特的地方,就是需要去重,一个用户一天内的多次访问只能算作一次。在 Redis 的集合类型中,Set 类型默认支持去重,所以看到有去重需求时,我们可能第一时间就会想到用 Set 类型,但是它会消耗很大的内存空间。Hash 类型也会消耗很大的内存空间。

    HyperLogLog 是一种用于统计基数的数据集合类型,它的最大优势就在于,当集合元素数量非常多时,它计算基数所需的空间总是固定的,而且还很小。

    在 Redis 中,每个 HyperLogLog 只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数。你看,和元素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。

    1. //redis命令
    2. PFADD page1:uv user1 user2 user3 user4 user5
    3. //去重统计
    4. PFCOUNT page1:uv
    1. HyperLogLogOperations hyperLogLogOperations = redisTemplate.opsForHyperLogLog();
    2. for(int i=0 ;i<10000;i++){
    3. hyperLogLogOperations.add("UV:20220803","uv"+i);
    4. }
    5. long count= hyperLogLogOperations.size("UV:20220803");

    不过,有一点需要你注意一下,HyperLogLog 的统计规则是基于概率完成的,所以它给出的统计结果是有一定误差的,标准误算率是 0.81%。这也就意味着,你使用 HyperLogLog 统计的 UV 是 100 万,但实际的 UV 可能是 101 万。虽然误差率不算大,但是,如果你需要精确统计结果的话,最好还是继续用 Set 或 Hash 类型。

    差集计算来说,只有 Set 支持。

    当需要进行排序统计时,List 中的元素虽然有序,但是一旦有新元素插入,原来的元素在 List 中的位置就会移动,那么,按位置读取的排序结果可能就不准确了。而 Sorted Set 本身是按照集合元素的权重排序,可以准确地按序获取结果,所以建议你优先使用它。

    如果我们记录的数据只有 0 和 1 两个值的状态,Bitmap 会是一个很好的选择,这主要归功于 Bitmap 对于一个数据只用 1 个 bit 记录,可以节省内存。

    对于基数统计来说,如果集合元素量达到亿级别而且不需要精确统计时,我建议你使用 HyperLogLog。

  • 相关阅读:
    部署LVS-NAT群集实验
    姿态分析开源工具箱MMPose使用示例:人体姿势估计
    为共创行业解决方案寻找黄金合作伙伴,百数低代码服务商模式在等您
    eCapture环境搭建编译|Android https明文抓包
    数仓:数仓建设中的数据建模和日志体系
    【华为OD机试真题 JS】英文输入法
    docker学习-常用命令
    python+Vue心理健康网站django
    uniapp微信小程序隐私保护引导新规
    消费力回落,名创优品大涨:深耕兴趣消费实现逆势增长
  • 原文地址:https://blog.csdn.net/zmm0420/article/details/126161803