(黑马点评)八、实现签到统计和uv统计

news/2024/11/13 15:28:48/

8.1 签到统计系列功能

8.1.1 认识BitMap结构

        BitMap是Redis基于String实现的一种高效的二进制数位的数据结构。因此一个BItMap的最大上线为512M,转为bit位可表示 2^32位

常见命令

SETBIT:向指定位置(offset)存入一个01

GETBIT :获取指定位置(offset)的bit

BITCOUNT :统计BitMap中值为1bit位的数量

BITFIELD :操作(查询、修改、自增)BitMapbit数组中的指定位置(offset)的值

BITFIELD_RO :获取BitMapbit数组,并以十进制形式返回

BITOP :将多个BitMap的结果做位运算(与 、或、异或)

BITPOS :查找bit数组中指定范围内第一个01出现的位置

 8.1.2 为什么要使用BitMap结构进行统计

传统的数据库表统计方式:十分耗费存储内存

采用二进制位的BitMap统计方式:高效、内存占用少、

key用来指定统计者的信息:姓名、编号、年月

vlaue采用二进制的0 | 1 串标识签到状况 ,0表示为未签到 1 表示为已签到

8.1.3 使用BitMap实现用户签到统计

按月来统计用户签到信息,签到记录为1,未签到则记录为0.

签到功能实现流程

1. 获取当前用户信息

2. 获取当前日期信息

3. 拼接业务key

4. 使用setBit(key,offset,value)实现签到打卡

    /*** 签到统计* @return*/@PostMapping("/sign")public Result sign(){return userService.sign();}/*** 签到功能* @return*/@Overridepublic Result sign() {//1. 获取当前的登陆用户UserDTO user = UserHolder.getUser();//2. 获取当前日期LocalDateTime now = LocalDateTime.now();//3. 拼接业务的keyString keySuffix = now.format(DateTimeFormatter.ofPattern("yyyy/MM"));String key =USER_SIGN_KEY +user.getNickName() +'_' + user.getId() +'_' + keySuffix;//4. 获取签到日期int day = now.getDayOfMonth();//5. 写入Redis SETBIT key offset ture/falsestringRedisTemplate.opsForValue().setBit(key, day-1, true);return Result.ok();}
直接发送签到请求

查看Redis写入的签到卡

 

8.1.4 使用BitMap实现用户连续签到天数统计

实现统计连续签到天数的流程

1. 获取当前登录用户信息

2. 获取日期信息

3. 拼接业务key

4. 使用 stringRedisTemplate.opsForValue()
                .bitField(key,
                        BitFieldSubCommands
                                .create()
                                .get(BitFieldSubCommands
                                        .BitFieldType
                                        .unsigned(dayOfMonth))
                                .valueAt(0)); 获取统计信息【10进制】

5. 判空

6. 循环右移 + 与1 运算 得出连续签到天数

7. 返回结果

/*** 连续签到天数统计* @return*/@GetMapping("/sign/count")public Result countSign(){return userService.countSign();}/*** 连续签到天数统计* @return*/@Overridepublic Result countSign() {//1. 获取当前登录用户UserDTO user = UserHolder.getUser();//2. 获取当前日期LocalDateTime now = LocalDateTime.now();int dayOfMonth = now.getDayOfMonth();//3.拼接业务的keyString keySuffix = now.format(DateTimeFormatter.ofPattern("yyyy/MM"));String key = USER_SIGN_KEY +user.getNickName() +'_' + user.getId() +'_' + keySuffix;//4. 获取当前用户签到数据(返回十进制数)List<Long> signNum = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));if(signNum == null || signNum.isEmpty()){return Result.ok(0);}Long num = signNum.get(0);if(num == null || num == 0){return Result.ok(0);}//5. 循环右移 + 1与运算 得出连续签到天数int dayCount = 0;while(true) {if(((num & 1) == 0)){break;}else{dayCount ++;}num = num >> 1;}return Result.ok(dayCount);}
发送连续签到统计请求

 

8.2 百万级UV统计功能实现

8.2.1 百万级UV统计的实现策略——HyperLogLog

什么是UV统计

UV:全称Unique Visitor,也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站,只记录1次。

什么是PV统计
PV:全称Page View,也叫页面访问量或点击量,用户每访问网站的一个页面,记录1次PV,用户多次打开页面,则记录多次PV。往往用来衡量网站的流量
 

传统方式实现UV统计面对的难题

UV统计在服务端做会比较麻烦,因为要判断该用户是否已经统计过了,需要将统计过的用户信息保存。但是如果每个访问的用户都保存到Redis中,数据量会非常恐怖。

高效的解决措施——使用HyperLogLog

        Hyperloglog(HLL)是从Loglog算法派生的概率算法,用于确定非常大的集合的基数,而不需要存储其所有值。

        Redis中的HLL是基于string结构实现的,单个HLL的内存永远小于16kb,内存占用低的令人发指!作为代价,其测量结果是概率性的,有小于0.81%的误差。不过对于UV统计来说,这完全可以忽略。

HyperLogLog的原理

哈希处理
        对每个要加入的元素进行哈希处理。这一步骤将元素映射为一个固定长度的二进制串。


划分与索引——分桶
        在 Redis 的实现中,使用 64bit 的 Hash 函数,其中 14bit 用于桶索引(将原始集合分成多个子集),剩下的 50bit 用于计算前导零的个数。这意味着 Redis 中的 HyperLogLog 使用16384个桶,每个桶可以表达的最大数字是:2^5+2^4+...+1 = 63 


估计基数——桶估计值公式
        通过使用补偿和线性计数的技术,将最大前导零位转换为基数估计值。具体的计算方法可以使用查表或其他数学模型来实现。在 Redis 的 HyperLogLog 实现中,有一个基于经验值的参数 alpha_m 用于调整估计值,以确保在大多数情况下都能提供较为准确的估计。


误差修正——偏差修正因子
由于 HyperLogLog 是一种概率性算法,其估计结果会存在一定的误差。Redis 通过应用修正公式来纠正这些估计误差,从而提供更加准确的基数估计。

合并与查询
        
HyperLogLog 支持多个数据集的并集操作,可以将多个 Key 的 UV 进行去重合并,且合并的复杂度是 O(1)。同时,获取一个或多个 HyperLogLog 结构的基数估计值也非常高效,查询的复杂度同样是 O(1)。
 

更多请看:HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的聪明的你可能会马上想到,用 HashMap 这种数 - 掘金 (juejin.cn)

8.2.2 使用HyperLogLog实现百万级UV统计测试

我们向Redis插入100万条数据,用来模拟超大UV访问量的统计,并计算使用HyperLogLog存储需要消耗的内存大小以及统计误差,从而向大家展示Hyper在统计方面的优秀性能

/*** 向Hyperloglog插入100万条数据进行测试,查看内存占用情况*/@Testvoid testHyperLogLog() {// 开始循环for(int i=0;i<1000;i++){String[] values = new String[1000];for(int j=0;j<1000;j++){values[j] = "user_" + i + "_" + j;}stringRedisTemplate.opsForHyperLogLog().add("hl2",values);}// 统计数量Long count = stringRedisTemplate.opsForHyperLogLog().size("hl2");System.out.println("count = " + count);}


http://www.ppmy.cn/news/1528372.html

相关文章

非标工业模型评审不再难,3D一览通助力高效协同

在当今工业领域&#xff0c;非标设备设计正成为满足特定客户需求的关键。这类设计服务涉及为特定应用场景量身定制的设备或机器&#xff0c;它们通常不是市场上现成的标准化产品&#xff0c;而是根据客户的独特需求进行个性化设计和制造。 这种定制化过程要求设计团队与客户进…

Git使用手册

1、初识Git 概述&#xff1a;Git 是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理项目版本管理。 知识点补充&#xff1a; 版本控制&#xff1a;一种记录一个或若干文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统。 分布式&#xff1a;每个人…

【D3.js in Action 3 精译_023】3.3 使用 D3 将数据绑定到 DOM 元素

当前内容所在位置&#xff1a; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可视化最佳实践&#xff08;下&#xff09;1.4 本…

python协程,线程,进程详细解释和使用

协程的概念与特点 1.1 协程的定义与历史 协程&#xff08;Coroutine&#xff09;是一种用户态的轻量级线程&#xff0c;它允许执行被挂起与恢复。与传统的子程序&#xff08;Subroutine&#xff09;不同&#xff0c;协程可以在执行过程中暂停&#xff0c;并在之后从暂停点继续…

PyTorch 图像分割模型教程

PyTorch 图像分割模型教程 在图像分割任务中&#xff0c;目标是将图像的每个像素归类为某一类&#xff0c;以分割出特定的物体。PyTorch 提供了非常灵活的工具&#xff0c;可以用于构建和训练图像分割模型。我们将使用 PyTorch 的经典网络架构&#xff0c;如 UNet 和 DeepLabV…

视频压缩篇:适用于 Windows 的 10 款最佳视频压缩器

视频压缩器现在对许多想要减小视频大小的视频编辑者来说非常有名。但是&#xff0c;并非所有可以在网上找到的视频压缩器都能产生最佳输出。因此&#xff0c;我们搜索了可以无损压缩视频的最出色的视频压缩器应用程序。本文列出了您可以在离线、在线和手机上使用的十大最佳视频…

海外大带宽服务器连接失败怎么办?

在全球化日益加深的今天&#xff0c;海外大带宽服务器已成为企业拓展国际市场、提升业务效率的重要工具。然而&#xff0c;面对复杂多变的网络环境和技术挑战&#xff0c;服务器连接失败的问题时有发生&#xff0c;这不仅影响了企业的正常运营&#xff0c;还可能带来经济损失和…

详解npm源及其使用方法

详解npm源及其使用方法 npm源是一个用于存储和提供npm包的服务器地址&#xff0c;npm在安装包时会通过这个源地址下载对应的依赖包。默认情况下&#xff0c;npm使用官方的npm源&#xff08;https://registry.npmjs.org/&#xff09;&#xff0c;该源存储了海量的Node.js开源包…