Redis大数据统计

news2024/9/24 18:05:19

文章目录

    • 一. 相关面试题
      • 1. 面试题一
      • 2. 面试题二
    • 二. 统计的类型
      • 1. 聚合统计
      • 2. 排序统计
      • 3. 二值统计
      • 4. 基数统计
    • 三. Hyperloglog
      • 1. 专业名词
      • 2. Hyperloglog使用
      • 3. Hyperloglog原理
      • 4. Hyperloglog案例
    • 四. GEO
      • 1. 面试题
      • 2. GEO使用
      • 3. GEO案例
    • 五. BitMap
      • 1. 面试题
      • 2. BitMap使用

一. 相关面试题

1. 面试题一

  • 抖音电商直播,主播介绍商品有评论,1个商品对应一系列评论,排序+展示+取前10条记录?
  • 用户在手机APP上的签到打卡信息:1天对应一系列用户的签到记录,新浪微博、钉钉打卡签到,没来如何统计?
  • 应用网站傻姑娘的网页浏览信息:1个网页对应一系列的点击访问,淘宝网首页,每天有多少人浏览首页
  • 你们公司系统上线后,说一下UV、PV和DAU分别是多少

2. 面试题二

  • 记录对集合中的数据进行统计

在移动应用中,需要统计每天的新增用户数和第二天的留存用户数
在电商网站的商品评论中,需要统计评论列表中的最新评论
在签到打卡中,需要统计一个月内连续打卡的用户数
在网页访问中,需要统计独立访客(UV)的量

痛点:

类似于今日头条、抖音、淘宝这样的用户访问级别是亿级的,请问如何处理?

上面问题的关键点就是,对于亿级数据的收集、清洗、统计和展现,如何存、如何取得快,真正有价值的是如何统计

二. 统计的类型

亿级系统中常见的统计有四种:

1. 聚合统计

统计多个集合元素的聚合结果,就是前面讲过的交差并等集合统计

  • 集合的差集运算: A − B A-B AB

属于A但不属于B的元素构成的集合

SDIFF key [key...]
  • 集合的并集运算: A ∪ B A \cup B AB

属于A或者属于B的元素合并后的集合

SUNION key[key...]
  • 集合的交集运算: A ∩ B A \cap B AB
SINTER key [key...]
SINTERCARD numkeys key[key ...] [LIMIT limit]

属于A同时也属于B的集

2. 排序统计

例如:抖音段视频最新评论留言的场景,请你设计一个列表。考察你的数据结构和设计思路

对于上面的需求我们可以使用zset数据结构

在这里插入图片描述

3. 二值统计

集合元素的取值就只有0和1两种,在钉钉上班签到打卡的场景中,我们只需要用记录有签到或没签到,此时可以用redis的bitmap数据结构。

4. 基数统计

统计一个集合中不重复的元素个数,这里使用redis的hyperloglog数据结构。

三. Hyperloglog

1. 专业名词

  • UV

Unique Visitor,独立访客,一般理解为客户端IP(需要去重考虑)

  • PV

Page View,页面访问量(不用去重)

  • DAU

Daily Active User,日活跃用户量,登陆或者使用了某个产品的用户数(去除重复登陆的用户),常用于反映网站、互联网应用或者网络游戏运营情况

  • MAU

Mouthly Active User,月活跃用户量

很多计数类场景,比如每日注册IP数,每日访问IP数,页面实时访问数,访问用户数,因为主要的目标是高效、巨量的进行计数,所以对存储的数据内容并步关心。

2. Hyperloglog使用

基数:是一种数据集,去重复后的真实个数。

去重复统计功能的基数估计算法就是HyperLogLog,它的优点是,在输入元素数量或者体积非常非常大的时候,计算基数的空间总是固定的,并且很小。在Redis里面,每个HyperLogLog只需要12kb的内存,就可以计算洁净2^64个的不同元素的基数,但是HyperLogLog只会根据输入元素来计算基数,而不会存储输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。

添加元素到 HyperLogLog:

PFADD key element [element ...]
PFADD my_hyperloglog a b c d e f g

获取 HyperLogLog 的基数估计值:

PFCOUNT key [key ...]
PFCOUNT my_hyperloglog

合并多个 HyperLogLog:

PFMERGE destkey sourcekey [sourcekey ...]

> PFADD hll1 a b c d
(integer) 1
> PFADD hll2 c d e f
(integer) 1
> PFMERGE hll_union hll1 hll2
OK
> PFCOUNT hll_union
(integer) 6

3. Hyperloglog原理

去重的思路:

  • hashset:在java中hashset就是一个无重复元素的集合(但是数据量很大不适合)
  • bitmap:

bitmap是通过用位bit数组来表示各个元素是否出现,每个元素队员一位,所需的总内存是N个bit。基数技术则嫁给你每一个元素对应到bit数组中的其中一位,新进入的元素只需要讲已经有的bit数组和新加入的元素进行按位或计算就行,这个方式能大大减少内存占用,且操作迅速。例如,假设一个样本案例就是一亿个基数位值数据,一个样本就是一亿,如果要统计1亿个数据的基数位值,大约需要内存1000000000/8/1024约为12M,内存减少占用的效果显著,这样得到的统计一个对象样本的基数值就是12M。但是,统计10000个对象样本(1w个亿级),就需要117.1875G,可见使用bitmaps还是不适合大数据量下(亿级)的基数计数场景。
但是bitmap的统计是精确的不会有误差

  • 概率算法:

通过牺牲准确率来换取空间,对于不要求绝对准确的场景下可以使用,因为概率算法不直接存储数据本身,通过一定的概率统计方法预估基数值,同时误差在一定范围内,由于又不存储故此可以大大节约内存。

HyperLogLog就是一种概率算法的体现

HyperLogLog只是进行不重复的基数统计,不是集合也不保存数据,只记录数量而不体现具体内容。它提供一种不精确的去重计数方案,误差大概在0.81%左右。

在这里插入图片描述
在这里插入图片描述

4. Hyperloglog案例

  • 需求

UV的统计需要去重,一个用户一天内的多次访问只能算做一次,淘宝、天猫首页的UV,平均每天是1~1.5亿左右。

  • 案例实现

Service

@Service
public class HyperLogLogService {
    @Autowired
    private RedisTemplate redisTemplate;

    @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);
                redisTemplate.opsForHyperLogLog().add("hll", ip);
                System.out.println("ip={" + ip + "}");
                try{
                    TimeUnit.SECONDS.sleep(3);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }, "t1").start();
    }
    public long UV(){
        return redisTemplate.opsForHyperLogLog().size("hll");
    }
}

Controller

@RestController
public class HyperLogLogController {
    @Autowired
    HyperLogLogService hyperLogLogService;

    @RequestMapping(value = "/uv",method = RequestMethod.GET)
    public long UV() {
        return hyperLogLogService.UV();
    }
}

统计结果
在这里插入图片描述

四. GEO

1. 面试题

移动互联网时代LBS应用越来越多,交友软件中附近的小姐姐,外卖软件中附近的美食店,打车软件附近的车辆等等,这种位置选择是如何实现的?

为什么不使用Mysql?

  • 查询性能问题,如果并发高,数据量大这种查询是会搞垮Mysql数据库的
  • 一般mysql查询是一个平面矩阵访问,而叫车服务是要以我为中心N公里为半径的圆形覆盖
  • 精确的问题,我们知道地球不是平面,而是一个球,这种矩形计算在长距离计算时会有很大误差

2. GEO使用

GEOADD 添加经纬度坐标

GEOADD key longitude latitude member [longitude latitude member ...]

> GEOADD cities 13.361389 38.115556 "Athens" 15.087269 37.502669 "Thessaloniki"
(integer) 2

GEO返回经纬度

GEORADIUS key longitude latitude radius m|km|mi|ft [WITHCOORD]

> GEORADIUS cities 15 37 200 km WITHCOORD
1) 1) "Thessaloniki"
   2) 1) "22.942600429058075"
      2) "40.640062264928304"

GEOHASH返回坐标的geohash表示

Geohash算法生成base32编码值,3维变为2维,2维变一维

GEOHASH key member [member ...]

> GEOADD cities 13.361389 38.115556 "Athens" 15.087269 37.502669 "Thessaloniki"
(integer) 2
> GEOHASH cities "Athens"
1) "sqdtr74hyu0"

GEODIST两个位置之间的距离

GEODIST key member1 member2 [unit]

> GEODIST cities "Athens" "Thessaloniki" km
"303.6469"

获取指定位置范围内的地理位置信息

GEORADIUS key longitude latitude radius m|km|mi|ft [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

> GEORADIUS cities 15 37 200 km
1) "Thessaloniki"

获取指定位置范围内的地理位置信息并按距离排序:

GEORADIUSBYMEMBER key member radius m|km|mi|ft [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

> GEORADIUSBYMEMBER cities "Athens" 200 km
1) "Athens"

3. GEO案例

  • 需求

以给定的经纬度为中心,找出某一半径内的元素

  • 案例实现

Controller

@RestController
public class GeoController {
    @Autowired
    private GeoService geoService;

    @RequestMapping(value = "/geoadd", method = RequestMethod.GET)
    public String geoAdd() {
        return geoService.geoAdd();
    }

    @RequestMapping(value = "/geopos", method = RequestMethod.GET)
    public Point position(String member) {
        return geoService.postion(member);
    }

    @RequestMapping(value = "/geohash", method = RequestMethod.GET)
    public String hash(String member) {
        return geoService.hash(member);
    }

    @RequestMapping(value = "/geodist", method = RequestMethod.GET)
    public Distance distance(String member1, String member2) {
        return geoService.distance(member1, member2);
    }

    @RequestMapping(value = "/georadius", method = RequestMethod.GET)
    public GeoResults radiusByxy() {
        return geoService.radiusByxy();
    }

    @RequestMapping(value = "/georadiusByMerber", method = RequestMethod.GET)
    public GeoResults radiusByMember() {
        return geoService.radiusByMember();
    }
}

Service

@Service
public class GeoService {

    public static final String CITY = "city";

    @Autowired
    private RedisTemplate redisTemplate;

    public String geoAdd() {
        Map<String, Point> map = new HashMap<>();
        map.put("天安门", new Point(116.403963, 39.915119));
        map.put("故宫", new Point(116.403414, 39.924091));
        map.put("长城", new Point(116.024067, 40.362639));
        map.put("北京大学", new Point(116.316833,39.998877));
        map.put("清华大学", new Point(116.333374,40.009645));
        redisTemplate.opsForGeo().add(CITY, map);
        return map.toString();
    }

    public Point postion(String member) {
        List<Point> position = redisTemplate.opsForGeo().position(CITY, member);
        return position.get(0);
    }

    public String hash(String member) {
        List<String> hash = redisTemplate.opsForGeo().hash(CITY, member);
        return hash.get(0);
    }

    public Distance distance(String member1, String member2) {
        Distance distance = redisTemplate.opsForGeo().distance(CITY, member1, member2, RedisGeoCommands.DistanceUnit.KILOMETERS);
        return distance;
    }

    public GeoResults radiusByxy() {
        //116.418038,39.919790
        Circle circle = new Circle(116.418038, 39.919790, Metrics.KILOMETERS.getMultiplier());
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusCommandArgs = RedisGeoCommands
                .GeoRadiusCommandArgs
                .newGeoRadiusArgs()
                .includeDistance()
                .includeCoordinates()
                .sortDescending();
        GeoResults<RedisGeoCommands.GeoLocation<String>> radius = redisTemplate.opsForGeo().radius(CITY, circle, geoRadiusCommandArgs);
        return radius;
    }

    public GeoResults radiusByMember() {
        Circle circle = new Circle(116.418038,39.919790,Metrics.KILOMETERS.getMultiplier());
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs()
                .includeDistance()
                .includeCoordinates()
                .sortDescending();
        GeoResults<RedisGeoCommands.GeoLocation<String>> tian = redisTemplate.opsForGeo().radius(CITY, "天安门", new Distance(50), args);
        return tian;
    }
}

测试结果
在这里插入图片描述
在这里插入图片描述

五. BitMap

1. 面试题

网站日活统计,连续签到打卡,最近一周日活统计,统计指定用户一年之内的登陆天数,某用户按照一年365天,哪天登录过,哪几天没登陆,全年中的登陆天数。

2. BitMap使用

在这里插入图片描述

  • 设置位
SETBIT key offset value

key: 位图的键名。
offset: 位图中的偏移量(从0开始)。
value: 要设置的值,可以是 0 或 1。

  • 获取位
GETBIT key offset

  • 统计位
BITCOUNT key [start end]
# 使用 BITCOUNT 命令统计位图中值为 1 的位的数量。
  • 位运算
BITOP operation destkey key [key ...]

operation: 要执行的位运算,可以是 AND、OR、XOR、NOT 中的一种。
destkey: 结果存放的键名。
key [key …]: 参与运算的位图键名。

bitmap的案例后面结合布隆过滤器来讲解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1483286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java+SpringBoot+Vue:招生宣传的全栈解决方案

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

《汇编语言》- 读书笔记 - 第13章-int 指令

《汇编语言》- 读书笔记 - 第13章-int 指令 13.1 int 指令13.2 编写供应用程序调用的中断例程中断例程&#xff1a;求一 word 型数据的平方主程序中断处理程序执行效果 中断例程&#xff1a;将一个全是字母&#xff0c;以0结尾的字符串&#xff0c;转化为大写主程序中断处理程序…

中央处理器CPU中的技术

1 知识加油站 1.1 cpu 指令的执行过程 取指&#xff1a;cpu 获取 程序计数器 中存放的指令地址。读取内存中此地址对应指令并存入指令寄存器译码&#xff1a;指令译码器&#xff0c;解析指令运行&#xff1a;算数逻辑单元计算回写&#xff1a;将执行结果写入对应位置 2. cpu…

如何使用ShellSweep检测特定目录中潜在的webshell文件

关于ShellSweep ShellSweep是一款功能强大的webshell检测工具&#xff0c;该工具使用了PowerShell、Python和Lua语言进行开发&#xff0c;可以帮助广大研究人员在特定目录中检测潜在的webshell文件。 ShellSweep由多个脚本模块组成&#xff0c;能够通过计算文件内容的熵来评估…

xsslabs第四关

测试 "onclick"alert(1) 这与第三关的代码是一样的&#xff0c;但是每一关考的点是不一样的所以我们看一下源代码 <!DOCTYPE html><!--STATUS OK--><html> <head> <meta http-equiv"content-type" content"text/html;ch…

C++string类讲解

大家好鸭 见字如面&#xff0c;已经有好久没有写文章了&#xff0c;这段时间忙着学习&#xff0c;也忙着玩&#xff0c;所以停更了一段时间 今天让我们来谈一谈关于C中的string类 什么是string类&#xff1f; 在c语言中我们操作字符串往往采用指针&#xff0c;这样的访问方式并…

不看后悔的腾讯云优惠券领取入口指南,2024最新代金券

腾讯云代金券领取渠道有哪些&#xff1f;腾讯云官网可以领取、官方媒体账号可以领取代金券、完成任务可以领取代金券&#xff0c;大家也可以在腾讯云百科蹲守代金券&#xff0c;因为腾讯云代金券领取渠道比较分散&#xff0c;腾讯云百科txybk.com专注汇总优惠代金券领取页面&am…

二级医院云HIS系统,云HIS源码,支持分院HIS,集团HIS

云HIS具有可扩展、易共享、易协同、低成本、体验号、更便捷、易维护的优势&#xff0c;重新定义了数字化医院信息系统&#xff0c;实现数字化医院信息系统的转型升级。云 HIS 系统功能完善&#xff0c;涵盖临床各业务部门&#xff0c;采集、抽提、汇总、存贮、展现所有的临床诊…

FL Studio 21.2.3.3586 for Mac中文版新功能介绍及2024年最新更新日志

如果你正计划学习音乐制作&#xff0c;一款强大且易学的音乐制作软件是必不可少的。由于很多小伙伴对音乐制作软件没有实际体验过&#xff0c;到底选择哪一款软件最合适成为当下最纠结的问题。 这里为大家推荐一款功能强大且适合新手小伙伴的音乐编曲软件—FL Studio 21.2.3.35…

[物联网] OneNet 多协议TCP透传

[物联网] OneNet 多协议TCP透传 STM32物联网–ONENET云平台的多协议接入产品创建 : https://blog.csdn.net/qq_44942724/article/details/134492924 Onenet tcp 透传 : https://blog.csdn.net/flyme2010/article/details/107086001 tcp服务端测试工具 : http://tcp.xnkiot.com/…

解决导入项目后在idea中不显示的问题

问题&#xff1a; 今天下午重新打开寒假之前负责的项目&#xff0c;发现打不开了&#xff0c; 从master拉取最新代码到我的分支&#xff0c;发现我的分支上显示就是这样子&#xff0c;无论怎么更新代码都不行。 原因&#xff1a; 在上一次上传代码的时候&#xff0c;我把我分…

YOLOv9改进|使用AKConv改进RepNCSPELAN4

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 一、改进点介绍 AKConv是一种具有任意数量的参数和任意采样形状的可变卷积核&#xff0c;对不规则特征有更好的提取效果。 RepNCSPELAN4是YOLOv9中的…

JAVA的学习日记

JAVA的学习日记&#xff08;2024.3.1&#xff09;&#xff08;b站韩顺平老师课程学习笔记版&#xff09; ps:捡起忘光光的Java语言 Sublime //1. public是公有&#xff0c;class是类 //2. public class Hello表示Hello是一个类&#xff0c;是一个public公有的类 //3. Hello{…

【框架】MyBatis 框架重点解析

MyBatis 框架重点解析 1. MyBatis 执行流程 会话工厂生产的 SqlSession 对象提供了对数据库执行SQL命令所需的所有方法&#xff0c;包括但不限于以下功能&#xff1a; 数据库操作&#xff1a;SqlSession可以执行查询&#xff08;select&#xff09;、插入&#xff08;insert&a…

桥接模式(Bridge Pattern) C++

上一节&#xff1a;适配器模式&#xff08;Adapter Pattern&#xff09; C 文章目录 0.理论1.组件2.使用场景 1.实践 0.理论 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;它的核心思想是将抽象部分与其实现部分分离&#xff0c;使它们可…

liunx安装jdk、redis、nginx

jdk安装 下载jdk,解压。 sudo tar -zxvf /usr/local/jdk-8u321-linux-x64.tar.gz -C /usr/local/ 在/etc/profile文件中的&#xff0c;我们只需要编辑一下&#xff0c;在文件的最后加上java变量的有关配置&#xff08;其他内容不要动&#xff09;。 export JAVA_HOME/usr/l…

操作系统系列学习——多进程图像

文章目录 前言多进程图像 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招&#xff0c;计划学习操作系统并完成6.0S81&#xff0c;加油&#xff01; 本文总结自B站【哈工大】操作系统 李治军&#xff08;全32讲&#xff09; 老师课程讲的非常好&#xff0c;感谢 【哈工大】…

设置文字之间的间距应该如何实现

设置文字之间的间距&#xff0c;通常指的是字母之间&#xff08;字符间距&#xff09;或单词之间的间距。在CSS中&#xff0c;这可以通过letter-spacing和word-spacing属性来实现。 字符间距&#xff08;letter-spacing&#xff09; letter-spacing属性用于调整字符之间的间距…

【Kotlin】函数

1 常规函数 1.1 无参函数 fun main() {myFun() }fun myFun() {println("myFun") // 打印: myFun } 1.2 有参函数 1&#xff09;常规调用 fun main() {myFun("myFun") // 打印: myFun }fun myFun(str: String) {println(str) } 2&#xff09;形参指定默…

C++:设计包含min 函数的栈

目录 题目 代码实现 输出 题目 定义栈的数据结构&#xff0c;要求添加一个min 函数&#xff0c;能够得到栈的最小元素。 要求函数min、push 以及pop 的时间复杂度都是O(1)。 代码实现 #include <iostream>template<typename T>class stack { public:stack() {…