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

embedded/2024/9/19 22:17:47/ 标签: redis, 学习, 黑马点评, 数据库, 阿里云

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/embedded/113938.html

相关文章

c语言动态内存分配

前言 我们已经掌握的内存开辟⽅式有&#xff1a; int val 20;//在栈空间上开辟四个字节 char arr[10] {0};//在栈空间上开辟10个字节的连续空间 但是上述的开辟空间的⽅式有两个特点&#xff1a; • 空间开辟⼤⼩是固定的。 • 数组在申明的时候&#xff0c;必须指定数组的…

什么?blender可以云渲染了!

在数字艺术和动画制作的世界里&#xff0c;Blender一直是自由和开源软件的佼佼者。它以其强大的功能和灵活性&#xff0c;赢得了全球艺术家和设计师的青睐。但你知道吗&#xff1f;现在&#xff0c;Blender也可以通过渲染 101云渲染来提高渲染效率了&#xff01; 渲染101云渲染…

Python 数学建模——Fitter 拟合数据样本的分布

文章目录 介绍代码实例 介绍 数学建模中很多时候&#xff0c;我们有某个随机变量 X X X 的若干样本 X 1 , X 2 , ⋯ , X n X_1,X_2,\cdots,X_n X1​,X2​,⋯,Xn​&#xff0c;想要还原随机变量 X X X 的概率密度函数 f ( x ) f(x) f(x)。诚然&#xff0c;高斯核密度估计可以…

solidity-19-fallback

接收ETH receive和fallback receive和callback是solidity中两个特殊的回调函数&#xff0c;一个处理接收ETH,一个处理不存在的函数调用。本质上就是吧fallback拆成了两个回调函数。我暂时不知道什么是fallback fallback调用不存在的函数时会被调用也就是这个函数是不是等价于…

Python基础(八)——MySql数据库

一.数据库 【库——>表——>数据】 借助数据库对数据进行组织存储&#xff0c;借助SQL语言对数据库、数据进行操作管理 Mysql数据库 下载&#xff1a;https://www.mysql.com/ 查看是否安装配置成功&#xff1a; 安装DBeaver用于Mysql数据库图形化 安装&#xff1a;…

MySQL——数据库的高级操作(一)数据备份与还原(1)数据的备份

在操作数据库时&#xff0c;难免会发生一些意外造成数据丢失。例如&#xff0c;突然停电、管理员的操作失误都可能导致数据的丢失。为了确保数据的安全&#xff0c;需要定期对数据库进行备份&#xff0c;这样&#xff0c;当遇到数据库中数据丢失或者出错的情况&#xff0c;就可…

Keil MDK5学习记录

2024.9.19 1. no browse information available in ‘xxx’的问题 成功解决Keil MDK5中no browse information available in ‘xxx’的问题-CSDN博客https://blog.csdn.net/bean_business/article/details/1091894452. .c文件中显示函数列表 如何在Keil5里.c文件中显示函数列表…

LeeCode打卡第二十九天

LeeCode打卡第二十九天 第一题&#xff1a;岛屿数量&#xff08;LeeCode第200题&#xff09;: 给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。岛屿总是被水包围&#xff0c;并且每座岛屿只…

Tcp三次握手四次挥手和SSL/TLS

1.Tcp三次握手四次挥手&#xff1a; 1.1基本概念&#xff1a; TCP&#xff08;三次握手和四次挥手&#xff09;是用于建立和终止可靠传输连接的过程。TCP协议是一种面向连接的传输层协议&#xff0c;确保数据在网络上可靠、有序地传输。下面详细解释三次握手和四次挥手的工作机…

浸没边界法精度相关的论文的阅读笔记

Convergence proof of the velocity field for a stokes flow immersed boundary method https://doi.org/10.1002/cpa.20233 研究对象的选取 他这里为什么能够选取一个周期性边界的流场啊&#xff1f;为什么不是狄利克雷边界或者诺伊曼边界&#xff1f; 方形流场的边界值 …

高级算法设计与分析 学习笔记6 B树

B树定义 一个块里面存了1000个数和1001个指针&#xff0c;指针指向的那个块里面的数据大小介于指针旁边的两个数之间 标准定义&#xff1a; B树上的操作 查找B树 创建B树 分割节点 都是选择正中间的那个&#xff0c;以免一直分裂。 插入数字 在插入的路上就会检查节点需不需要…

Testbench编写与Vivado Simulator的基本操作

Testbench编写与Vivado Simulator的基本操作 Testbench编写 Testbench 是一种用Verilog或者systemVerilog语言编写的程序或模块&#xff0c;编写testbench的主要目的是为了对使用硬件描述语言&#xff08;HDL&#xff09;设计的电路UUT(unit under test)进行仿真验证&#xf…

一招教你解决excel表格打印预览时候表格线条显示不全的问题

1、如图&#xff0c;我们在制作好excel表格后再需要打印时候&#xff0c;点击打印预览会出现以下情况&#xff1a; 最下边的表格线条显示不全&#xff0c;这样即使打印出来或者导出为pdf&#xff0c;文件中依然显示不全&#xff0c;这时候我们只需要在excel表格中轻轻设置一下就…

CleanMyMac X 4.15.6正式版 mac直装破解版

你知道 CleanMyMac是什么吗&#xff1f;它的字面意思为“清理我的Mac”&#xff0c;作为软件&#xff0c;那就是一款 Mac清理工具 &#xff0c;Mac OS X 系统下知名系统清理软件&#xff0c;是数以万计的Mac用户的选择。它可以流畅地与系统性能相结合&#xff0c;只需…

JVM 运行时数据区域

目录 前言 程序计数器 java虚拟机栈 本地方法栈 java堆 方法区 运行时常量池 前言 首先, java程序在被加载在内存中运行的时候, 会把他自己管理的内存划分为若干个不同的数据区域, 就比如你是一个你是一个快递员, 一堆快递过来需要你分拣, 这个时候, 你就需要根据投放的目…

三、(JS)JS中常见的表单事件

一、onfocus、onblur事件 这个很容易理解&#xff0c;就不解释啦。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"&…

关于 Goroutines 和并发控制的 Golang 难题

下面是一道关于 Goroutines 和并发控制的 Golang 难题&#xff0c;它涉及到 Go 的并发编程模型、Goroutines、通道&#xff08;Channels&#xff09;以及 sync.WaitGroup 的使用&#xff1a; 问题描述&#xff1a; 你有一个需要并发执行的任务&#xff0c;其中有 100 个 URL …

基于Spring Boot的学生社区故障维修预约系统的设计与实现(开题报告)

毕业论文(设计)开题报告 基于Spring Boot的学生社区故障维修预约系统设计与实现 姓 名 学 院 数学与数据科学学院 专业班级 信息与计算科学202 学 号 202021314223 校内指导教师 职称/职务 副教授 校外指导教师 职称/职务 技术经理 起始时间 2023年9月 教务部制 一、开…

【LLM多模态】文生视频评测基准VBench

note VBench的16个维度自动化评估指标代码实践&#xff08;待完成&#xff09;16个维度的prompt举例人类偏好标注&#xff1a;计算VBench评估结果与人类偏好之间的相关性、用于DPO微调 文章目录 note一、相关背景二、VBench评测基准概述&#xff1a;论文如何解决这个问题&…

Go语言现代web开发13 方法和接口

方法 As you probably noticed, there are no classes in the Go programming language. But we can mimic this by declaring functions on types. The type which declares functions is called the receiver argument and the function declared on the type is called the…