当前位置: 首页 > news >正文

redis实战-实现用户签到UV统计

BitMap功能演示

我们针对签到功能完全可以通过mysql来完成,比如说以下这张表

用户一次签到,就是一条记录,假如有1000万用户,平均每人每年签到次数为10次,则这张表一年的数据量为 1亿条

每签到一次需要使用(8 + 8 + 1 + 1 + 3 + 1)共22 字节的内存,一个月则最少需要600多字节,这种方案内存消耗过大

我们可以采用类似这样的方案来实现我们的签到需求。

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

把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路就称为位图(BitMap)。这样我们就用极小的空间,来实现了大量数据的表示

Redis中是利用string类型数据结构实现BitMap,因此最大上限是512M,转换为bit则是 2^32个bit位。

BitMap的操作命令有:

  • SETBIT:向指定位置(offset)存入一个0或1

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

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

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

  • BITFIELD_RO :获取BitMap中bit数组,并以十进制形式返回

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

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

使用setbit进行赋值,用于设置签到状态,未赋值的会初始化成0

 使用getbit来获取签到状态

实现签到功能

需求:实现签到接口,将当前用户当天签到信息保存到Redis中

思路:我们可以把年和月作为bitMap的key,然后保存到一个bitMap中,每次签到就到对应的位上把数字从0变成1,只要对应是1,就表明说明这一天已经签到了,反之则没有签到

UserController

 @PostMapping("/sign")public Result sign(){return userService.sign();}

UserServiceImpl

由于前端没有传递相应的时间参数,我们只需要在后端自己获取即可

@Override
public Result sign() {// 1.获取当前登录用户Long userId = UserHolder.getUser().getId();// 2.获取日期LocalDateTime now = LocalDateTime.now();// 3.拼接keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;// 4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();// 5.写入Redis SETBIT key offset 1stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return Result.ok();
}

由于今天是五号,从左往右数第五位就是1

签到统计

从最后一次签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,就是连续签到天数。

Java逻辑代码:获得当前这个月的最后一次签到数据,定义一个计数器,然后不停的向前统计,直到获得第一个非0的数字即可,每得到一个非0的数字计数器+1,直到遍历完所有的数据,就可以获得当前月的签到总天数了

假设今天是10号,那么我们就可以从当前月的第一天开始,获得到当前这一天的位数,是10号,那么就是10位,去拿这段时间的数据,就能拿到所有的数据了,那么这10天里边签到了多少次呢?统计有多少个1即可。 我们只需要执行以下的redis命令即可

BITFIELD key GET u[dayOfMonth] 0

我们还要解决如何从后向前遍历这些比特位的问题

注意:bitMap返回的数据是10进制,哪假如说返回一个数字8,那么我哪儿知道到底哪些是0,哪些是1呢?我们只需要让得到的10进制数字和1做与运算就可以了,因为1只有遇见1 才是1,其他数字都是0 ,我们把签到结果和1进行与操作,每与一次,就把签到结果向右移动一位,依次内推,我们就能完成逐个遍历的效果了。

需求:实现下面接口,统计当前用户截止当前时间在本月的连续签到天数

有用户有时间我们就可以组织出对应的key,此时就能找到这个用户截止这天的所有签到记录,再根据这套算法,就能统计出来他连续签到的次数了

UserController

@GetMapping("/sign/count")
public Result signCount(){return userService.signCount();
}

UserServiceImpl

@Override
public Result signCount() {// 1.获取当前登录用户Long userId = UserHolder.getUser().getId();// 2.获取日期LocalDateTime now = LocalDateTime.now();// 3.拼接keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;// 4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();// 5.获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字 BITFIELD sign:5:202203 GET u14 0List<Long> result = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));if (result == null || result.isEmpty()) {// 没有任何签到结果return Result.ok(0);}Long num = result.get(0);if (num == null || num == 0) {return Result.ok(0);}// 6.循环遍历int count = 0;while (true) {// 6.1.让这个数字与1做与运算,得到数字的最后一个bit位  // 判断这个bit位是否为0if ((num & 1) == 0) {// 如果为0,说明未签到,结束break;}else {// 如果不为0,说明已签到,计数器+1count++;}// 把数字右移一位,抛弃最后一个bit位,继续下一个bit位num >>>= 1;}return Result.ok(count);
}

UV统计

相关概念 

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

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

通常来说UV会比PV大很多,所以衡量同一个网站的访问量,我们需要综合考虑很多因素,所以我们只是单纯的把这两个值作为一个参考值

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

Hyperloglog(HLL)是从Loglog算法派生的概率算法,用于确定非常大的集合的基数,而不需要存储其所有值。相关算法原理大家可以参考:HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的 - 掘金 Redis中的HLL是基于string结构实现的,单个HLL的内存永远小于16kb内存占用低的令人发指!作为代价,其测量结果是概率性的,有小于0.81%的误差。不过对于UV统计来说,这完全可以忽略。

 百万数据测试

测试代码

   String[] users = new String[1000];int index = 0;for (int i = 1; i < 1000000; i++) {index = i % 1000;users[index++] = "user_" + i;if (i % 1000 == 0) {index = 0;stringRedisTemplate.opsForHyperLogLog().add("hills", users);}}Long size = stringRedisTemplate.opsForHyperLogLog().size("hills");System.out.println("size=" + size);

未测试前redis的内存空间

 测试之后,发现确实内存消耗不大

相关文章:

redis实战-实现用户签到UV统计

BitMap功能演示 我们针对签到功能完全可以通过mysql来完成&#xff0c;比如说以下这张表 用户一次签到&#xff0c;就是一条记录&#xff0c;假如有1000万用户&#xff0c;平均每人每年签到次数为10次&#xff0c;则这张表一年的数据量为 1亿条 每签到一次需要使用&#xff08…...

作为创始人的价值观与心法,构建系统

价值观 绿色、健康、有趣、感恩、谦卑、责任、勇气、客观、冷静、自洽、尊重、价值、服务、善良、利他 作为co-founder衡量的一个很重要的标准就是这个人的人品&#xff0c;大家一起做事情的体验要好&#xff0c;才能有large energy&#xff0c;且流通。 乐观、通达&#xf…...

Go语言基础面经

1.go语言编程的好处是什么 编译和运行都很快。 在语言层级支持并行操作。 有垃圾处理器。 内置字符串和 maps。 函数是 go 语言的最基本编程单位。 2.说说go语言的select机制 select 机制用来处理异步 IO 问题 select 机制最大的一条限制就是每个 case 语句里必须是一个…...

服务器文件备份

服务器上&#xff0c;做好跟应用程序有关的文件备份&#xff08;一般备份到远程的盘符&#xff09;&#xff0c;有助于当服务器发生硬件等故障时&#xff0c;可以对系统进行进行快速恢复。 下面以Windows服务器为例&#xff0c;记录如何做文件的备份操作。 具体操作如下&#…...

剑指offer——JZ68 二叉搜索树的最近公共祖先 解题思路与具体代码【C++】

一、题目描述与要求 二叉搜索树的最近公共祖先_牛客题霸_牛客网 (nowcoder.com) 题目描述 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 1.对于该题的最近的公共祖先定义:对于有根树T的两个节点p、q&#xff0c;最近公共祖先LCA(T,p,q)表示一个节点x&#…...

[Spring] @Bean 修饰方法时如何注入参数

目录 一、Bean 的简单使用 1、正常情况 2、问题提出 二、解决方案 1、Qualifier 2、直接写方法名 三、特殊情况 1、DataSource 一、Bean 的简单使用 在开发中&#xff0c;基于 XML 文件配置 Bean 对象的做法非常繁琐且不好维护&#xff0c;因此绝大部分情况下都是使用…...

docker拉取镜像错误 missing signature key

您正在尝试使用 yum 在 CentOS 或 RHEL 系统上安装 docker-ce&#xff0c;但您遇到了一些问题。根据您提供的输出&#xff0c;这里有几个需要注意的点&#xff1a; 系统未注册: "This system is not registered with an entitlement server" 指示您的系统未注册。对于…...

基于可解释性特征矩阵与稀疏采样全局特征组合的人体行为识别

论文还未发表&#xff0c;不细说&#xff0c;欢迎讨论。 Title: A New Solution to Skeleton-Based Human Action Recognition via the combination usage of explainable feature extraction and sparse sampling global features. Abstract: With the development of deep …...

OpenCV4(C++)—— 仿射变换、透射变换和极坐标变换

文章目录 一、仿射变换1. getRotationMatrix2D()2. warpAffine() 二、透射变换三、极坐标变换 一、仿射变换 在OpenCV中没有专门用于图像旋转的函数&#xff0c;而是通过图像的仿射变换实现图像的旋转。实现图像的旋转首先需要确定旋转角度和旋转中心&#xff0c;之后确定旋转…...

http.header.Set()与Add()区别;

在Go语言中进行HTTP请求时&#xff0c;http.Header对象表示HTTP请求或响应的头部信息。http.Header是一个map[string][]string类型的结构&#xff0c;用于存储键值对&#xff0c;其中键表示HTTP头字段的名称&#xff0c;值是一个字符串切片&#xff0c;可以存储多个相同名称的头…...

vue-7-vuex

一、Vuex 概述 目标&#xff1a;明确Vuex是什么&#xff0c;应用场景以及优势 1.是什么 Vuex 是一个 Vue 的 状态管理工具&#xff0c;状态就是数据。 大白话&#xff1a;Vuex 是一个插件&#xff0c;可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。例如&#xff1a;购…...

SSO单点登录和OAuth2.0区别

一、概述 SSO是Single Sign On的缩写&#xff0c;OAuth是Open Authority的缩写&#xff0c;这两者都是使用令牌的方式来代替用户密码访问应用。流程上来说他们非常相似&#xff0c;但概念上又十分不同。SSO大家应该比较熟悉&#xff0c;它将登录认证和业务系统分离&#xff0c…...

【轻松玩转MacOS】基本操作篇

引言 本文是系列的开篇&#xff0c;我将为大家介绍MacOS的基本操作。对于初次接触MacOS的用户来说&#xff0c;掌握这些基本操作是必不可少的。无论是启动和关机&#xff0c;还是使用键盘和鼠标&#xff0c;或者是快捷键的使用&#xff0c;这些基本操作都是你开始使用MacOS的第…...

华为ICT——第三章图像处理基本任务

目录 1&#xff1a;数字图像处理的层次&#xff1a;&#xff08;处理-分析-理解&#xff09;顺序不能错&#xff1a; 2&#xff1a;图像处理&#xff08;图像处理过程&#xff09;&#xff1a; 3&#xff1a;图像分析&#xff08;特征提取&#xff09;&#xff1a; 4&#x…...

(C++)引用的用法总结

引用&#xff08;reference&#xff09;是C极为重要的一部分&#xff0c;本文对其用法进行简单总结。 1. 引用的基本用法 引用的关键字为&&#xff0c;表示取地址的意思&#xff0c;引用变量定义如下&#xff1a; int m 1; int &n m; //定义 cout<<"n:…...

Charles:移动端抓包 / windows客户端 iOS手机 / 手机访问PC本地项目做调试

一、背景描述 1.1、本文需求&#xff1a;移动端进行抓包调试 1.2、理解Charles可以做什么 Charles是一款跨平台的网络代理软件&#xff0c;可以用于捕获和分析网络流量&#xff0c;对HTTP、HTTPS、HTTP/2等协议进行调试和监控。使用Charles可以帮助开发人员进行Web开发、调试…...

【AI】深度学习——人工智能、深度学习与神经网络

文章目录 0.1 如何开发一个AI系统0.2 表示学习(特征处理)0.2.1 传统特征学习特征选择过滤式包裹式 L 1 L_1 L1​ 正则化 特征抽取监督的特征学习无监督的特征学习 特征工程作用 0.2.2 语义鸿沟0.2.3 表示方式关联 0.2.4 表示学习对比 0.3 深度学习0.3.1 表示学习与深度学习0.3.…...

RK3288:BT656 RN6752调试

这篇文章主要想介绍一下再RK3288平台上面调试BT656 video in的注意事项。以RN6752转接芯片&#xff0c;android10平台为例进行介绍。 目录 1. RK3288 VIDEO INPUT 并口 2. 驱动调试 2.1 RN6752 驱动实现 ①rn6752_g_mbus_config总线相关配置 ②rn6752_querystd配置制式 …...

LLMs 蒸馏, 量化精度, 剪枝 模型优化以用于部署 Model optimizations for deployment

现在&#xff0c;您已经了解了如何调整和对齐大型语言模型以适应您的任务&#xff0c;让我们讨论一下将模型集成到应用程序中需要考虑的事项。 在这个阶段有许多重要的问题需要问。第一组问题与您的LLM在部署中的功能有关。您需要模型生成完成的速度有多快&#xff1f;您有多…...

Milvus踩坑笔记

本文用于记录在学习 Milvus文档时所遇到的一些Bug或报错及解决方法 参考文章&#xff1a; 官方demo&#xff1a;在Dynamic Schema的集合中插入数据 报错1&#xff1a;auto id enabled, id shouldnt in entities[0] 问题描述 此报错出现在Milvus官方在介绍 Dynamic Schema …...

什么是轴电流?轴电流对轴承有什么危害?

根据同步发电机结构及工作原理&#xff0c;由于定子铁芯组合缝、定子硅钢片接缝&#xff0c;定子与转子空气间隙不均匀&#xff0c;轴中心与磁场中心不一致等&#xff0c;机组的主轴不可避免地要在一个不完全对称的磁场中旋转。这样&#xff0c;在轴两端就会产生一个交流电压。…...

react create-react-app v5配置 px2rem (不暴露 eject方式)

环境信息&#xff1a; create-react-app v5 “react”: “^18.2.0” “postcss-plugin-px2rem”: “^0.8.1” 配置步骤&#xff1a; 不暴露 eject 配置自己的webpack&#xff1a; 1.下载react-app-rewired 和 customize-cra-5 npm install react-app-rewired customize-cra…...

.net中用标志位解决socket粘包问题

以下为wpf中, 用标志位"q" 解决粘包问题 using MyFrameWorkWpf.Entities; using System.Collections.ObjectModel; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.…...

【Ubuntu】Systemctl 管理 MinIO 服务器的启动和停止

要使用 systemctl 来管理 MinIO 服务器的启动和停止&#xff0c;您需要创建一个 systemd 服务单元文件&#xff0c;以便 systemd 能够启动和停止 MinIO 服务器。下面是一般的步骤&#xff1a; 创建 systemd 服务单元文件&#xff1a; 打开终端并使用文本编辑器创建一个新的 sys…...

《golang设计模式》第二部分·结构型模式-07-代理模式(Proxy)

文章目录 1. 概述1.1 角色1.2 模式类图 2. 代码示例2.1 设计2.2 代码2.3 示例类图 1. 概述 代理&#xff08;Proxy&#xff09;是用于控制客户端访问目标对象的占位对象。 需求&#xff1a;在调用接口实现真是主题之前需要一些提前处理。 解决&#xff1a;写一个代理&#xff…...

Jmeter常用线程组设置策略

一、前言 在JMeter压力测试中&#xff0c;我们时常见到的几个场景有&#xff1a;单场景基准测试、单场景并发测试、单场景容量测试、混合场景容量测试、混合场景并发测试以及混合场景稳定性测试 在本篇文章中&#xff0c;我们会用到一些插件&#xff0c;在这边先给大家列出&…...

【Spring】Spring MVC 程序开发

Spring MVC 程序开发 一. 什么是 Spring MVC1. MVC2. Spring、Spring Boot 与 Spring MVC 二. 创建 Spring MVC 项目1. 创建项目2. 用户和程序的映射3. 获取用户请求参数①. 获取单个参数②. 获取多个参数③. 传递对象④. 后端参数重命名&#xff08;后端参数映射&#xff09;R…...

如何在企业网站里做好网络安全

在当今数字时代&#xff0c;网站不仅仅是企业宣传和产品展示的平台&#xff0c;更是日常生活和商业活动中不可或缺的一部分。然而&#xff0c;随着网络技术不断发展&#xff0c;网站的安全问题日益凸显。保护网站和用户数据的安全已经成为至关重要的任务&#xff0c;以下是一些…...

windows server 2012 服务器打开系统远程功能

服务器上开启远程功能 进入服务器&#xff0c;选择“添加角色和功能” 需要选择安装的服务器类型&#xff0c;如图所示 然后在服务器池中选择你需要使用的服务器。 选择完成后&#xff0c;在图示列表下勾选“远程桌面服务” 再选择需要安装的功能和角色服务。 选择完成确认内容…...

智能工厂MES系统,终端设备支持手机、PDA、工业平板、PC

一、开源项目简介 源计划智能工厂MES系统(开源版) 功能包括销售管理&#xff0c;仓库管理&#xff0c;生产管理&#xff0c;质量管理&#xff0c;设备管理&#xff0c;条码追溯&#xff0c;财务管理&#xff0c;系统集成&#xff0c;移动端APP。 二、开源协议 使用GPL-3.0开…...