redis大数据统计之hyperloglog,GEO,Bitmap

news2024/11/27 10:27:27

目录

一、亿级系统常见的四中统计

1、聚合统计

2、排序统计

3、二值统计

4、基数统计

二、hyperloglog

去重的方式有哪些?

hyperloglog实战演示

1、技术选型

2、代码演示 

三、GEO

GEO实战演示

四、Bitmap


一、亿级系统常见的四中统计

1、聚合统计

聚合统计:统计多个集合元素的聚合结果,交并差集和聚合函数的应用。用于推广社交

2、排序统计

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

3、二值统计

二值统计:用于签到打卡,使用bitmap

4、基数统计

基数统计:指统计一个集合中不重复的元素个数,使用hyperloglog

统计名词

UV:Unique Visitor 独立访客,一般伟客户端IP(需要去重)

PV:Page View 页面浏览量(不去重)

DAU:Daily Active User 日活跃用户,登录或者使用了某个产品的用户数(去除重复登录的用户)

常用于反映网站、互联网应用或者网络游戏的运营情况。

MAU:Monthly Active User 月活跃用户

二、hyperloglog

需求分析

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

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

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

去重的方式有哪些?

1、HashSet

2、bitmap

bitmap是通过用位bit数组来表示各元素是否出现,每个元素对应一位,所需的总内存为N个bit。
基数计数则将每一个元素对应到bit数组中的其中一位,比如bit数组010010101(按照从零开始下标,有的就是1、4、6、8)。新进入的元素只需要将已经有的bit数组和新加入的元素进行按位或计算就行。这个方式能大大减少内存占用且位操作迅速。但是对于亿级统计不太合适。

3、hyperloglog

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

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

hyperloglog实战演示

示例:淘宝网站首页亿级UV的Redis统计方案

1、UV的统计需要去重,一个用户一天内的多次访问只能算作一次。
2、淘宝、天猫首页的UV,平均每天是1~1.5个亿左右。
3、每天存1.5个亿的IP,访问者来了后先去查是否存在,不存在加入。

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、代码演示 

HypeLogLogService

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;

@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;


@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();
    }
}

运行结果,统计每个ip访问的次数 

三、GEO

面试题

移动互联网时代LBS应用越来越多,交友软件中附近的小姐姐、外卖软件中附近的美食店铺、打车

软件附近的车辆等等。那这种附近各种形形色色的XXX地址位置选择是如何实现的?

mysql会有什么问题呢?

1.查询性能问题,如果并发高,数据量大这种查询会搞垮mysql数据库

2.一般mysql查询的是一个平面矩形访问,而叫车服务要以我为中心N公里为半径的圆形覆盖。I

3.精准度的问题,我们知道地球不是平面坐标系,而是一个圆球,这种矩形计算在长距离计算时会

有很大误差,mysql不合适。

GEO常用命令

GEOADD:添加经纬度坐标,该命令用于将一个或多个指定的地理位置(经纬度)添加到指定的

键中。其中,key 是存储位置信息的键,longitude 和 latitude 是经度和纬度,member 是与该位置

相关联的成员。

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

GEOPOS:返回经纬度,该命令用于获取指定成员的经纬度坐标。其中,key 是存储位置信息的

键,member 是需要获取经纬度的成员。

GEOPOS key member [member ...]

GEOHASH:返回坐标的geohash表示,该命令用于获取指定成员的 geohash 表示。其中,key 是

存储位置信息的键,member 是需要获取 geohash 的成员。

GEOHASH key member [member ...]

GEODIST:两个位置之间的距离,该命令用于计算两个位置之间的距离。其中,key 是存储位置信息的键,member1 和 member2 是需要计算距离的两个成员,unit 是可选参数,用于指定距离的单位,默认为米。

GEODIST key member1 member2 [unit]

GEORADIUS:根据给定的经纬度获取范围内的位置 该命令用于根据给定的中心点经纬度和半径,在指定的键中获取范围内的位置信息。其中,key 是存储位置信息的键,longitude 和 latitude 是中心点的经纬度,radius 是半径,单位可以是 m(米)、km(千米)、ft(英尺)或 mi(英里),WITHCOORD、WITHDIST、WITHHASH 和 COUNT 是可选参数,用于指定返回结果的附加信息和数量限制。

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

GEORADIUSBYMEMDBER:根据给定的成员获取范围内的位置 该命令用于根据给定的成员,在指定的键中获取范围内的位置信息。其中,key 是存储位置信息的键,member 是指定的成员,radius 是半径,单位可以是 m(米)、km(千米)、ft(英尺)或 mi(英里),WITHCOORD、WITHDIST、WITHHASH 和 COUNT 是可选参数,用于指定返回结果的附加信息和数量限制。

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

GEO实战演示

美团地图位置附近的酒店推送

需求分析:美团附近酒店,附近的人,一公里以内的各种营业厅,饭店,附近共享单车等等。

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

GEOService


public interface GeoService {
    String geoAdd();

    Point position(String member);

    String hash(String member);

    Distance distance(String member1, String member2);

    GeoResults radiusByxy();

    GeoResults radiusMember();

}

GEOServiceImpl

import com.xfcy.service.GeoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;


import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class GeoServiceImpl implements GeoService {

    public static final String CITY = "city";

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public String geoAdd() {
        Map<String, Point> map = new HashMap<>();
        map.put("白马寺", new Point(112.610356,34.728481));
        map.put("龙门石窟", new Point(112.484071,34.564375));
        map.put("老君山", new Point(111.663,33.75186));
        redisTemplate.opsForGeo().add(CITY, map);
        return null;
    }

    @Override
    public Point position(String member) {
        // 获取经纬度坐标
        List<Point> position = redisTemplate.opsForGeo().position(CITY, member);
        return position.get(0);
    }

    @Override
    public String hash(String member) {
        // geohash 算法生成的base32编码
        List<String> hash = redisTemplate.opsForGeo().hash(CITY, member);
        return hash.get(0);
    }

    @Override
    public Distance distance(String member1, String member2) {
        // 获取两个给定位置之间的距离
        Distance distance = redisTemplate.opsForGeo().distance(CITY, member1, member2,
                RedisGeoCommands.DistanceUnit.KILOMETERS);
        return distance;
    }

    @Override
    public GeoResults radiusByxy() {
        // 通过经纬度查找附近的,白马寺的位置 112.610356,34.728481
        Circle circle = new Circle(112.610356, 34.728481, Metrics.KILOMETERS.getMultiplier());
        // 返回50条
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeCoordinates().sortDescending().limit(50);

        GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = redisTemplate.opsForGeo().radius(CITY, circle, args);
        return geoResults;
    }

    @Override
    public GeoResults radiusMember() {
        // 通过地方查找附近,洛阳白马寺为例
        String member = "白马寺";
        // 返回10条
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().sortAscending().limit(10);
        // 半径 10 公里内
        Distance distance = new Distance(10, Metrics.KILOMETERS);
        GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = redisTemplate.opsForGeo().radius(CITY, member, distance, args);

        return geoResults;
    }
}

GEOController

import com.xfcy.service.GeoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
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;
import org.springframework.data.geo.Point;

@Api(tags = "美团地图位置附近的酒店推送GEO")
@Slf4j
@RestController
public class GeoController {

    @Resource
    private GeoService geoService;

    @ApiOperation("添加经纬度坐标")
    @RequestMapping(value = "/geoadd",method = RequestMethod.GET)
    public String geoAdd() {
        return geoService.geoAdd();
    }

    @ApiOperation("获取经纬度坐标geopos")
    @RequestMapping(value = "/geopos",method = RequestMethod.GET)
    public Point position(String member){
        return geoService.position(member);
    }

    @ApiOperation("获取经纬度生成的base32编码值geohash")
    @RequestMapping(value = "/geohash",method = RequestMethod.GET)
    public String hash(String member){
        return geoService.hash(member);
    }

    @ApiOperation("获取两个给定位置之间的距离")
    @RequestMapping(value = "/geodist",method = RequestMethod.GET)
    public Distance distance(String member1, String member2){
        return geoService.distance(member1,member2);
    }

    @ApiOperation("通过经纬度查找洛阳白马寺附近的")
    @RequestMapping(value = "/georadius",method = RequestMethod.GET)
    public GeoResults radiusByxy(){
        return geoService.radiusByxy();
    }

    @ApiOperation("通过地方查找附近,白马寺为例")
    @RequestMapping(value = "/georadiusByMember",method = RequestMethod.GET)
    public GeoResults radiusMember(){
        return geoService.radiusMember();
    }

}

四、Bitmap

Bitmap:由 0 和 1 状态表现得二进制位的 bit 数组

作用:日活统计,连续签到打卡,近一周活跃用户,统计用户一年登录天数,电影、广告是否被点击播放过。

案例:京东签到领取京东

1、小厂方法,传统 mysql 方式

CREATE TABLE user_sign
(
  keyid BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  user_key VARCHAR(200),#京东用户ID
  sign_date DATETIME,#签到日期(20210618)
  sign_count INT #连续签到天数
)
 
INSERT INTO user_sign(user_key,sign_date,sign_count)
VALUES ('20210618-xxxx-xxxx-xxxx-xxxxxxxxxxxx','2020-06-18 15:11:12',1);
 
SELECT
    sign_count
FROM
    user_sign
WHERE
    user_key = '20210618-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
    AND sign_date BETWEEN '2020-06-17 00:00:00' AND '2020-06-18 23:59:59'
ORDER BY
    sign_date DESC
    LIMIT 1;
 

问题:签到量用户小这个可以,用户量很大就会出现问题

如何解决?

  • 一条签到记录对应一条记录,会占据越来越大的空间。

  • 一个月最多31天,刚好我们的int类型是32位,那这样一个int类型就可以搞定一个月,32位大于31天,当天来了位是1没来就是0。

  • 一条数据直接存储一个月的签到记录,不再是存储一天的签到记录。

大厂方法

  • 基于redis的 Bitmap 实现签到日历

  • 在签到统计时,每个用户一天的签到用1个bit位就能表示,

  • 一个月(假设是31天)的签到情况用31个bit位就可以,一年的签到也只需要用365个bit位,根本不用太复杂的集合类型

基本命令

SETBIT key offset value    // 将第offset的值设为value  value只能是0或1  offset 从0开始
GETBIT key offset        // 获得第offset位的值
STRLEN key              // 得出占多少字节 超过8位后自己按照8位一组一byte再扩容
BITCOUNT key         // 得出该key里面含有几个1
BITOP and destKey key1 key2 // 对一个或多个 key 求逻辑并,并将结果保存到 destkey 
BITOP or destKey key1 key2 // 对一个或多个 key 求逻辑或,并将结果保存到 destkey 
BITOP XOR destKey key1 key2 // 对一个或多个 key 求逻辑异或,并将结果保存到 destkey 
BITOP NOT destKey key1 key2 // 对一个或多个 key 求逻辑非,并将结果保存到 destkey 

 

 

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

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

相关文章

服务器和云计算之间有什么关系?

云计算与服务器之间的关系是密切而复杂的。首先&#xff0c;我们需要明确一点&#xff0c;云计算并不是一种全新的技术&#xff0c;而是对现有技术的一种整合和改进。在这个基础上&#xff0c;我们可以更好地理解云计算与服务器之间的关系。 服务器是云计算的重要组成部分之一…

SpringCloud-生产者和消费者

一、生产者和消费者的定义 在 Spring Cloud 中&#xff0c;术语 "生产者" 和 "消费者" 用于描述微服务架构中的两种基本角色。 角色定义生产者 Provider生产者是提供具体服务或功能的模块。它将业务逻辑封装成服务&#xff0c;供其他模块调用。生产者向服…

线性表 —— 数组、栈、队、链表

本文以 typescript 实现数据结构&#xff0c;虽说是 ts 实现&#xff0c;但更准确说是面向对象的方式实现&#xff0c;因此可以无缝切换成 Java 等面向对象语言。 什么是数据结构&#xff08;Data Structure&#xff09;&#xff1f; “数据结构是ADT&#xff08;抽象数据类型…

一文学会Axios的使用

异步请求 同步发送请求过程如下 浏览器页面在发送请求给服务器&#xff0c;在服务器处理请求的过程中&#xff0c;浏览器页面不能做其他的操作。只能等到服务器响应结束后才能&#xff0c;浏览器页面才能继续做其他的操作。 异步发送请求过程如下浏览器页面发送请求给服务器&…

蓝桥杯第八届省赛题笔记------基于单片机的电子钟程序设计与调试

题目要求&#xff1a; 一、基本要求 1.1 使用 CT107D 单片机竞赛板&#xff0c;完成“电子钟”功能的程序设计与调试&#xff1b; 1.2 设计与调试过程中&#xff0c;可参考组委会提供的“资源数据包”&#xff1b; 1.3 Keil 工程文件以准考证号命名&#xff0c;保存在…

032-安全开发-JavaEE应用Servlet路由技术JDBCMybatis数据库生命周期

032-安全开发-JavaEE应用&Servlet路由技术&JDBC&Mybatis数据库&生命周期 #知识点&#xff1a; 1、JavaEE-HTTP-Servlet技术 2、JavaEE-数据库-JDBC&Mybatis 演示案例&#xff1a; ➢JavaEE-HTTP-Servlet&路由&周期 ➢JavaEE-数据库-JDBC&Mybat…

MiniCPM:揭示端侧大语言模型的无限潜力

技术博客链接&#xff1a; &#x1f517;https://shengdinghu.notion.site/MiniCPM ➤ Github地址&#xff1a; &#x1f517;https://github.com/OpenBMB/MiniCPM ➤ Hugging Face地址&#xff1a; &#x1f517;https://huggingface.co/openbmb/MiniCPM-2B-sft-bf16 1 …

目标检测及相关算法介绍

文章目录 目标检测介绍目标检测算法分类目标检测算法模型组成经典目标检测论文 目标检测介绍 目标检测是计算机视觉领域中的一项重要任务&#xff0c;旨在识别图像或视频中的特定对象的位置并将其与不同类别中的对象进行分类。与图像分类任务不同&#xff0c;目标检测不仅需要…

工信部颁发的《计算机视觉处理设计开发工程师》中级证书

计算机视觉&#xff08;Computer Vision&#xff09;是一门研究如何让计算机能够理解和分析数字图像或视频的学科。简单来说&#xff0c;计算机视觉的目标是让计算机能够像人类一样对视觉信息进行处理和理解。为实现这个目标&#xff0c;计算机视觉结合了图像处理、机器学习、模…

RabbitMQ——基于 KeepAlived + HAProxy 搭建 RabbitMQ 高可用负载均衡集群

一、集群简介 1.1 集 群架构 当单台 RabbitMQ 服务器的处理消息的能力达到瓶颈时&#xff0c;此时可以通过 RabbitMQ 集群来进行扩展&#xff0c;从而达到提升吞吐量的目的。 RabbitMQ 集群是一个或多个节点的逻辑分组&#xff0c;集群中的每个节点都是对等的&#xff0c;每…

职业性格测试在求职应聘跳槽中的应用

人的性格总是千奇百怪&#xff0c;有的人总是想迎接挑战&#xff0c;超越自己&#xff0c;不停的奔着高处走&#xff0c;然而有的人总是喜欢随遇而安&#xff0c;踏踏实实一辈子&#xff0c;有份安稳的工作&#xff0c;有吃有喝就好。那么对于哪些喜欢迎接挑战&#xff0c;但又…

Adobe Camera Raw for Mac v16.1.0中文激活版

Adobe Camera Raw for Mac是一款强大的RAW格式图像编辑工具&#xff0c;它能够处理和编辑来自各种数码相机的原始图像。以下是关于Adobe Camera Raw for Mac的一些主要特点和功能&#xff1a; 软件下载&#xff1a;Adobe Camera Raw for Mac v16.1.0中文激活版 RAW格式支持&…

MyBatis一些常见知识点!

什么是 ORM 框架&#xff1f; MyBatis 有哪些优缺点&#xff1f; 典型回答&#xff1a; ORM&#xff08;Object-Relational Mapping&#xff0c;对象关系映射&#xff09;框架是一种将关系型数据库中的数据 与 应用程序中的对象进行映射的技术。它通过在程序代码中定义的类和属…

Unity类银河恶魔城学习记录1-12 PlayerComboAttack源代码 P39

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili PlayerPrimaryAttack.cs using System.Collections; using System.Collect…

七月论文审稿GPT第2.5版:微调GPT3.5 turbo 16K和llama2 13B以扩大对GPT4的优势

前言 我司自去年7月份成立大模型项目团队以来&#xff0c;至今已有5个项目组&#xff0c;其中 第一个项目组的AIGC模特生成系统已经上线在七月官网第二项目组的论文审稿GPT则将在今年3 4月份对外上线发布第三项目组的RAG知识库问答第1版则在春节之前已就绪至于第四、第五项目…

LLaVA:GPT-4V(ision) 的新开源替代品

LLaVA&#xff1a;GPT-4V(ision) 的新开源替代品。 LLaVA &#xff08;https://llava-vl.github.io/&#xff0c;是 Large Language 和Visual A ssistant的缩写&#xff09;。它是一种很有前景的开源生成式 AI 模型&#xff0c;它复制了 OpenAI GPT-4 在与图像对话方面的一些功…

(5)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—12种聚类算法说明与归纳

目录 一、12种聚类(无监督学习)算法说明和区分比较 聚类算法的类型(一) ​编辑导入函数库 加载数据集 ​编辑 (1)K-Means --Centroid models (2)Mini-Batch K-Means -- Centroid models (3)AffinityPropagation (Hierarchical) -- Connectivity models (4)Mean Shift…

最新GPT4.0使用教程,AI绘画,GPT语音对话使用,DALL-E3文生图

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

算法练习-三数之和(思路+流程图+代码)

难度参考 难度&#xff1a;中等 分类&#xff1a;数组 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。且所在课程未提供测试平台&#xff0c;故实现代码主要为自行测试的那种&#xff0c;以下内容均为个人笔记&#xff0c;旨在…

openGauss学习笔记-214 openGauss 性能调优-确定性能调优范围

文章目录 openGauss学习笔记-214 openGauss 性能调优-确定性能调优范围214.1 性能因素214.2 调优范围确定 openGauss学习笔记-214 openGauss 性能调优-确定性能调优范围 数据库性能调优通常发生在用户对业务的执行效率不满意&#xff0c;期望通过调优加快业务执行的情况下。正…