Redis7高级之案例实战 hyperloglog(五)

5.1 系统中常见的四种统计

1.聚合统计

统计多个集合元素的聚合结果(交并差集合统计) set集合

在这里插入图片描述

2.排序统计

抖音短视频最新评论留言的场景,设计一个展现列表

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

3.二值统计

集合元素的取值就只有 0 和 1 ,来记录签到还是没签到 bitmap

4.基数统计

只统计一个集合中不重复的元素个数 hyperloglog

5.2hyperloglog

1.名词解释

  • UV (Unique Visitor) 独立访客,一般理解为客户端IP (需要考虑去重
  • PV (Page View) 页面浏览量,不用去重
  • DAU(Daily Active User)日活跃用户量,登录或者使用了某个产品的用户数(去重复登录的用户),常用于反映网站、互联网应用或者网络游戏的运营情况
  • MAU(Monthly Active User)月活跃用户量

2.需求

很多计数类场景,比如 每日注册 IP 数、每日访问 IP 数、页面实时访问数 PV、访问用户数 UV等。

因为主要的目标高效、巨量地进行计数,所以对存储的数据的内容并不太关心。

也就是说它只能用于统计巨量数量,不太涉及具体的统计对象的内容和精准性。

统计单日一个页面的访问量(PV),单次访问就算一次。

统计单日一个页面的用户访问量(UV),即按照用户为维度计算,单个用户一天内多次访问也只算一次。

多个key的合并统计,某个门户网站的所有模块的PV聚合统计就是整个网站的总PV。

3.hyperloglog复习

  • 基数
    • 是一种数据集,去重复后的真实个数
    • 在Redis里面,每个HyperLogLog键只需要花费12KB内存,就可以计算接近2的64次方个不同元素的基数

在这里插入图片描述

在这里插入图片描述

  • 基数统计就是 HyperLogLog

    • 去重复统计功能

      • hashSet
      • bitmap
        • bitmap是通过用位bit数组来表示各元素是否出现,每个元素对应一位,所需的总内存为N个bit。
        • 新进入的元素只需要将已经有的bit数组和新加入的元素进行按位或计算就行。这个方式能大大减少内存占用且位操作迅速。
        • 如果数据较大,比如一个样本案例就是一亿个基数拉值数据,一个样本就是一亿,如果要统计一亿个数据的基数拉值,大约需要内存 1100000000/8/1024/1024 约等于 12M,内存减少占用的效果显著。这样得到统计一个对象样本的基数值需要12M。
        • 如果统计10000个对象样本(1w个亿级),就需要117.1875G将近120G,可见使用bitmaps还是不适用大数据量下(亿级)的基数计数场景,
        • 但是bitmap是精确计算的
      • 结论
        • 量变会引起质变
      • 办法
        • 概率算法
          • 通过牺牲准确率来换取空间,对于不要求绝对准确率的场景下可以使用,因为概率算法不直接存储数据本身,通过牺牲准确率来换取空间,对于不要求绝对准确率的场景下可以使用,因为概率算法不直接存储数据本身
          • HyperLogLog就是一种概率算法的实现。
    • 原理说明

      • 只是进行不重复的基数统计,不是集合也不保存数据,只记录数量而不是具体内容

      • 有误差

        • hyperloglog提供不精确的去重技术方案
        • 牺牲准确率来换取空间,误差仅仅只是 0.81% 左右

5.3统计亿级UV的Redis方案

1.对于技术的选型

  • 用mysql
    • mysql扛不住稍微大一点的并发,而且都需要存入mysql中,导致mysql的检索也会变慢
  • 用redis的hash结构存储
    • 按照ipv4的结构来说明,一个ip最多15个字节(ip=“192.168.238.1xx”),某一天 1.5亿*15个字节 = 2G,一个月60G,内存直接没了,加内存条都没用
  • hyperloglog
    • 在Redis里面,每个HyperLogLog键只需要花费12KB内存,就可以计算接近2的64次方个不同元素的基数

在这里插入图片描述

2.编码(yml和pom与上次一致)

  • HypeLogLogService

    /**
     * @author 晓风残月Lx
     * @date 2023/3/27 20:18
     */
    public interface HypeLogLogService {
          
          
    
        public long uv();
    }
    
    
  • HyperLogLogServiceImpl

    import com.xfcy.service.HypeLogLogService;
    
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.Resource;
    import java.util.Random;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author 晓风残月Lx
     * @date 2023/3/27 20:18
     */
    @Slf4j
    @Service
    public class HypeLogLogServiceImpl implements HypeLogLogService {
          
          
    
        @Resource
        private RedisTemplate redisTemplate;
    
        /**
         * 模拟后台有用户点击网站首页,每个用户来自不同的IP地址
         *
         * @PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。
         */
        @PostConstruct
        public void initIp() {
          
          
            new Thread(() -> {
          
          
                String ip = null;
                for (int i = 0; i < 200; i++) {
          
          
                    Random random = new Random();
                    ip = random.nextInt(256) + "." + random.nextInt(256) + "." + random.nextInt(256) + "." + random.nextInt(256);
    
                    Long hll = redisTemplate.opsForHyperLogLog().add("hll", ip);
    
                    log.info("ip = {}, 该IP地址访问首页的次数={}",ip,hll);
    
                    try {
          
          
                        // 暂停3秒钟
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
          
          
                        throw new RuntimeException(e);
                    }
                }
            }, "t1").start();
        }
    
        public long uv() {
          
          
            // PFCOUNT
            return redisTemplate.opsForHyperLogLog().size("hll");
        }
    }
    
  • HyperLogLogController

    import com.xfcy.service.HypeLogLogService;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    /**
     * @author 晓风残月Lx
     * @date 2023/3/27 20:17
     */
    @Api(tags = "淘宝亿级UV的Redis统计方案")
    @Slf4j
    @RestController
    public class HyperLogLogController {
          
          
    
        @Resource
        private HypeLogLogService hypeLogLogService;
    
    
        @ApiOperation("获得IP去重复后的UV统计访问量")
        @RequestMapping(value = "/uv",method = RequestMethod.GET)
        public long uv(){
          
          
            return hypeLogLogService.uv();
        }
    }
    
  • 启动项目后测试

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_55993923/article/details/129869087