16.Redis 高级数据类型 + 网站数据统计

目录

1.Redis 高级数据类型

2.网站数据统计

2.1 业务层

2.2 表现层

2.2.1 记录数据

2.2.2 查看数据


1.Redis 高级数据类型

HyperLogLog:采用一种基数算法,用于完成独立总数的统计;占据空间小,无论统计多少个数据,只占12K的内存空间;不精确的统计算法,标准误差为 0.81%

Bitmap:不是一种独立的数据结构,实际上就是字符串;支持按位存取数据,可以将其看成是 byte 数组;适合存储索大量的连续的数据的布尔值

统计 20万个重复数据的独立总数

    // 统计20万个重复数据的独立总数.@Testpublic void testHyperLogLog() {String redisKey = "test:hll:01";for (int i = 1; i <= 100000; i++) {redisTemplate.opsForHyperLogLog().add(redisKey, i);}//再次循环 10万次for (int i = 1; i <= 100000; i++) {int r = (int) (Math.random() * 100000 + 1);redisTemplate.opsForHyperLogLog().add(redisKey, r);}long size = redisTemplate.opsForHyperLogLog().size(redisKey);//统计去重数据的数量System.out.println(size);}

将3组数据合并,再统计合并后的重复数据的独立总数

    @Testpublic void testHyperLogLogUnion() {String redisKey2 = "test:hll:02";for (int i = 1; i <= 10000; i++) {redisTemplate.opsForHyperLogLog().add(redisKey2, i);}String redisKey3 = "test:hll:03";for (int i = 5001; i <= 15000; i++) {redisTemplate.opsForHyperLogLog().add(redisKey3, i);}String redisKey4 = "test:hll:04";for (int i = 10001; i <= 20000; i++) {redisTemplate.opsForHyperLogLog().add(redisKey4, i);}String unionKey = "test:hll:union";redisTemplate.opsForHyperLogLog().union(unionKey, redisKey2, redisKey3, redisKey4);long size = redisTemplate.opsForHyperLogLog().size(unionKey);System.out.println(size);}

统计一组数据的布尔值

    @Testpublic void testBitMap() {String redisKey = "test:bm:01";// 记录redisTemplate.opsForValue().setBit(redisKey, 1, true);redisTemplate.opsForValue().setBit(redisKey, 4, true);redisTemplate.opsForValue().setBit(redisKey, 7, true);// 查询System.out.println(redisTemplate.opsForValue().getBit(redisKey, 0));System.out.println(redisTemplate.opsForValue().getBit(redisKey, 1));System.out.println(redisTemplate.opsForValue().getBit(redisKey, 2));// 统计Object obj = redisTemplate.execute(new RedisCallback() {@Overridepublic Object doInRedis(RedisConnection connection) throws DataAccessException {return connection.bitCount(redisKey.getBytes());}});System.out.println(obj);}

统计3组数据的布尔值, 并对这3组数据做OR运算

    @Testpublic void testBitMapOperation() {String redisKey2 = "test:bm:02";redisTemplate.opsForValue().setBit(redisKey2, 0, true);redisTemplate.opsForValue().setBit(redisKey2, 1, true);redisTemplate.opsForValue().setBit(redisKey2, 2, true);String redisKey3 = "test:bm:03";redisTemplate.opsForValue().setBit(redisKey3, 2, true);redisTemplate.opsForValue().setBit(redisKey3, 3, true);redisTemplate.opsForValue().setBit(redisKey3, 4, true);String redisKey4 = "test:bm:04";redisTemplate.opsForValue().setBit(redisKey4, 4, true);redisTemplate.opsForValue().setBit(redisKey4, 5, true);redisTemplate.opsForValue().setBit(redisKey4, 6, true);String redisKey = "test:bm:or";Object obj = redisTemplate.execute(new RedisCallback() {@Overridepublic Object doInRedis(RedisConnection connection) throws DataAccessException {connection.bitOp(RedisStringCommands.BitOperation.OR,redisKey.getBytes(), redisKey2.getBytes(), redisKey3.getBytes(), redisKey4.getBytes());return connection.bitCount(redisKey.getBytes());}});System.out.println(obj);System.out.println(redisTemplate.opsForValue().getBit(redisKey, 0));System.out.println(redisTemplate.opsForValue().getBit(redisKey, 1));System.out.println(redisTemplate.opsForValue().getBit(redisKey, 2));System.out.println(redisTemplate.opsForValue().getBit(redisKey, 3));System.out.println(redisTemplate.opsForValue().getBit(redisKey, 4));System.out.println(redisTemplate.opsForValue().getBit(redisKey, 5));System.out.println(redisTemplate.opsForValue().getBit(redisKey, 6));}

2.网站数据统计

  • UV(Unique Visitor):独立访问,需要通过用户 IP 排重统计数据;每次访问都要进行统计;HyperLogLog 性能好,且存储空间小
  • DAU(Daily Active User):日活跃用户,需要通过用户 ID 排重统计数据;访问过一次,则认为其活跃;Bitmap 性能好且可以统计精确的结果

使用 Redis,定义 RedisKey,打开 RedisKeyUtil 类添加

  • 添加两个前缀:uv、dau
  • 添加方法:获取单日uv、传入日期字符串,返回 前缀 + 分隔符 + 日期
  • 添加方法:获取区间uv(从哪天到哪天),传入开始日期,结束日期,返回 前缀 + 分隔符 + 开始日期 + 分隔符 + 结束日期
  • 添加方法:获取单日活跃用户,传入日期,返回 前缀 + 分隔符 + 日期
  • 添加方法:获取区间活跃用户,传入日期,返回 前缀 + 分隔符 + 开始日期 + 分隔符 + 结束日期
    //UV(Unique Visitor):独立访问private static final String PREFIX_UV = "uv";//DAU(Daily Active User):日活跃用户private static final String PREFIX_DAU = "dau";//单日UVpublic static String getUVKey(String date) {return PREFIX_UV + SPLIT + date;}//区间UVpublic static String getUVKey(String startDate, String endDate) {return PREFIX_DAU + SPLIT + startDate + SPLIT + endDate;}// 单日活跃用户public static String getDAUKey(String date) {return PREFIX_DAU + SPLIT + date;}// 区间活跃用户public static String getDAUKey(String startDate, String endDate) {return PREFIX_DAU + SPLIT + startDate + SPLIT + endDate;}

2.1 业务层

在 service 包下新建 DataService 类:

  • 注入 RedisTemplate
  • 在统计的时候,需要使用到日期(格式化成年月日的形式),实例化一个 SimpleDateFormat
  • 统计数据:首先记录数据,在每次请求当中截获请求,把相关数据记录到 Redis 中;其次,在查看的时候提供一个查询的方法
  • 处理 UV 的统计:构造方法,将指定的 IP 计入 UV(传入 IP)——得到 key记录到 Redis 中
  • 构造方法,统计指定的日期范围内的 UV:传入(开始日期、结束日期),把范围内每一天的 key 做一个合并得到某一组的 key,封装成集合;遍历日期,需要对日期做运算,实例化 Calender,包含开始日期做遍历。遍历完成之后合并数据并且返回统计的结果
  • 将指定用户计入 DAU:首先得到 key,传入当前时间,然后存入 Redis 中
  • 统计指定日期范围内的 DAU:同理上述(日期范围内每一天的 DAU 之间做 or运算:假设统计今天的活跃用户,只需要今天访问就代表活跃;假设以一周为单位,则这一周任意一次访问即活跃)
package com.example.demo.service;import com.example.demo.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;/*** 网站数据统计:UV、DAU*/
@Service
public class DataService {@Autowiredprivate RedisTemplate redisTemplate;//在统计的时候,需要使用到日期(格式化成年月日的形式),实例化一个 SimpleDateFormatprivate SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");// 将指定的IP计入UVpublic void recordUV(String ip) {//得到 key记录到 Redis 中String redisKey = RedisKeyUtil.getUVKey(df.format(new Date()));redisTemplate.opsForHyperLogLog().add(redisKey, ip);}// 统计指定日期范围内的UVpublic long calculateUV(Date start, Date end) {if (start == null || end == null) {throw new IllegalArgumentException("参数不能为空!");}// 整理该日期范围内的key//把范围内每一天的 key 做一个合并得到某一组的 key,封装成集合;// 遍历日期,需要对日期做运算,实例化Calender,包含开始日期做遍历。遍历完成之后合并数据并且返回统计的结果List<String> keyList = new ArrayList<>();Calendar calendar = Calendar.getInstance();calendar.setTime(start);while (!calendar.getTime().after(end)) {String key = RedisKeyUtil.getUVKey(df.format(calendar.getTime()));keyList.add(key);calendar.add(Calendar.DATE, 1);}// 合并这些数据String redisKey = RedisKeyUtil.getUVKey(df.format(start), df.format(end));redisTemplate.opsForHyperLogLog().union(redisKey, keyList.toArray());// 返回统计的结果return redisTemplate.opsForHyperLogLog().size(redisKey);}// 将指定用户计入DAUpublic void recordDAU(int userId) {String redisKey = RedisKeyUtil.getDAUKey(df.format(new Date()));redisTemplate.opsForValue().setBit(redisKey, userId, true);}// 统计指定日期范围内的DAUpublic long calculateDAU(Date start, Date end) {if (start == null || end == null) {throw new IllegalArgumentException("参数不能为空!");}// 整理该日期范围内的keyList<byte[]> keyList = new ArrayList<>();Calendar calendar = Calendar.getInstance();calendar.setTime(start);while (!calendar.getTime().after(end)) {String key = RedisKeyUtil.getDAUKey(df.format(calendar.getTime()));keyList.add(key.getBytes());calendar.add(Calendar.DATE, 1);}// 进行OR运算return (long) redisTemplate.execute(new RedisCallback() {@Overridepublic Object doInRedis(RedisConnection connection) throws DataAccessException {String redisKey = RedisKeyUtil.getDAUKey(df.format(start), df.format(end));connection.bitOp(RedisStringCommands.BitOperation.OR,redisKey.getBytes(), keyList.toArray(new byte[0][0]));return connection.bitCount(redisKey.getBytes());}});}}

2.2 表现层

什么时候记录数据(拦截器)、查看数据

2.2.1 记录数据

在 controller 包下的 interceptor 包下新建 DataInterceptor 类

  • 实现 HandlerInterceptor 接口
  • 记录 UV、DAU 需要注入 DataService
  • 活跃用户需要注入 HostHolder
  • 在请求初期机型统计,重写 perHandle
package com.example.demo.controller.interceptor;import com.example.demo.entity.User;
import com.example.demo.service.DataService;
import com.example.demo.util.HostHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class DataInterceptor implements HandlerInterceptor {@Autowiredprivate DataService dataService;@Autowiredprivate HostHolder hostHolder;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 统计UVString ip = request.getRemoteHost();dataService.recordUV(ip);// 统计DAUUser user = hostHolder.getUser();if (user != null) {dataService.recordDAU(user.getId());}return true;}
}

在 WebMvcConfig 类中设置拦截器:

    @Autowiredprivate MessageInterceptor messageInterceptor;registry.addInterceptor(dataInterceptor).excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");

2.2.2 查看数据

在 controller 类包下新建 DataController 类:

  • 添加三个方法:访问统计页面、统计网站 UV、统计活跃用户
  • 访问统计页面:添加访问路径,方法中需要返回模板路径
  • 统计网站 UV:添加访问路径(提交两个日期按钮相当于提交表单,是一个 POST 请求),传入开始、结束日期以及模板,使用注解@DateTimeFormat(pattern = "yyyy-MM-dd"),设置日期格式。统计结果返回给模板的时候,网站 UV保留开始和结束的年月日格式,最后返回到模板
  • 统计活跃用户:同理
package com.example.demo.controller;import com.example.demo.service.DataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;import java.util.Date;/*** 网站数据统计:UV、DAU*/
@Controller
public class DataController {@Autowiredprivate DataService dataService;// 统计页面@RequestMapping(path = "/data", method = {RequestMethod.GET, RequestMethod.POST})public String getDataPage() {return "/site/admin/data";}// 统计网站UV@RequestMapping(path = "/data/uv", method = RequestMethod.POST)public String getUV(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start,@DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model) {long uv = dataService.calculateUV(start, end);model.addAttribute("uvResult", uv);model.addAttribute("uvStartDate", start);model.addAttribute("uvEndDate", end);return "forward:/data";}// 统计活跃用户@RequestMapping(path = "/data/dau", method = RequestMethod.POST)public String getDAU(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start,@DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model) {long dau = dataService.calculateDAU(start, end);model.addAttribute("dauResult", dau);model.addAttribute("dauStartDate", start);model.addAttribute("dauEndDate", end);return "forward:/data";}}

最后处理 data.html

​​​​​​​

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

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

相关文章

最新技术整理3款开源免费直播推流工具,实现实时视频推流、视频拉流,目标端可以是服务器、云平台、移动设备等(附源码)

最新技术整理3款开源免费直播推流工具&#xff0c;实现实时视频推流、视频拉流&#xff0c;目标端可以是服务器、云平台、移动设备等&#xff08;附源码&#xff09;。 什么是推流&#xff1f; 视频推流是指将实时的视频数据从一个源端发送到一个或多个目标端的过程。推流的源…

数据库原理及应用·存储过程和触发器

12.1 T-SQL中的变量 12.1.1 T-SQL概述 SQL&#xff08;Structured Query Language&#xff09;结构化查询语言&#xff0c;是一种数据库查询和程序设计语言&#xff0c;用于存取数据以及查询、更新和管理关系数据库系统。 Transact-SQL即事务SQL&#xff0c;也简称为T-SQL&a…

Gateway集成方法以及拦截器和过滤器的使用

前提&#xff1a;请先创建好一个SpringBoot项目 1. 引入依赖 SpringCloud 和 alibabaCloud 、 SpringBoot间对版本有强制要求&#xff0c;我使用的springboot是3.0.2的版本。版本对应关系请看&#xff1a;版本说明 alibaba/spring-cloud-alibaba Wiki GitHub <dependency…

(windows2012共享文件夹和防火墙设置

windows2012共享文件夹和防火墙设置 1.windows2012文件夹共享1.共享和高级共享的区别![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/0d815cc6862a4c7a99be11442fb5d950.png#pic_center) 2.windows的防火墙设置1.防火墙设置8080端口让tomot可以在主机可以访问1.新建…

中间件系列 - Redis入门到实战(高级篇-最佳实践)

前言 学习视频&#xff1a; 黑马程序员Redis入门到实战教程&#xff0c;深度透析redis底层原理redis分布式锁企业解决方案黑马点评实战项目中间件系列 - Redis入门到实战本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删除学习目标 Redis键值设计批处理优化服…

电商数据分析-03-电商数据采集

参考 最最最全数据仓库建设指南&#xff0c;速速收藏&#xff01;&#xff01; 第1章 数据仓库概念 数据仓库规划 1.1 数仓搭建 我们这里所说的数据仓库&#xff0c;是基于大数据体系的&#xff0c;里面包含标签类目&#xff0c;区别于传统的数据仓库。下面我们来将这张图分解…

STM32 支持IAP的bootloader开发,使用串口通过Ymodem协议传输固件

资料下载: https://download.csdn.net/download/vvoennvv/88658447 一、概述 关于IAP的原理和Ymodem协议&#xff0c;本文不做任何论述&#xff0c;本文只论述bootloader如何使用串口通过Ymodem协议接收升级程序并进行IAP升级&#xff0c;以及bootloader和主程序两个工程的配置…

dxbuilder关于开发一款国产数据库建模软件的思考与行动

一、背景 随着一声紧急的呼叫&#xff0c;快快快。把你们有安装PownerDesigner的软件都卸载掉&#xff0c;公司被发律师函了&#xff0c;这是来自于领导的呼喊。 我们公司大部分的软件的数据结构&#xff0c;都是用PownerDesigner来进行设计的。以便进行后期的管理与维护。不…

Vue学习之第一、二章——Vue核心与组件化编程

第一章. Vue核心 1.1 Vue简介 1.1.1 官网 英文官网: https://vuejs.org/中文官网: https://cn.vuejs.org/ 1.1.2 Vue特点 遵循 MVVM 模式编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发它本身只关注 UI, 也可以引入其它第三方库开发项目 1.2 初始Vue 这里可以参考&a…

ios 之 数据库、地理位置、应用内跳转、推送、制作静态库、CoreData

第一节&#xff1a;数据库 常见的API SQLite提供了一系列的API函数&#xff0c;用于执行各种数据库相关的操作。以下是一些常用的SQLite API函数及其简要说明&#xff1a;1. sqlite3_initialize:- 初始化SQLite库。通常在开始使用SQLite之前调用&#xff0c;但如果没有调用&a…

“京东”数据包暴雷——李逵还是李鬼?

大家好&#xff0c;我是吴军&#xff0c;一家软件技术开发公司的产品经理。 前几个月市面上出现了一个京东数据包的项目&#xff0c;乍一听还蛮正规的&#xff0c;强子不卖货&#xff0c;去做数据服务了&#xff1f;他究竟是怎么一个盈利方式&#xff1f;到底是李逵还是李鬼&a…

【新资讯】《网络安全事件报告管理办法(征求意见稿)》正在公开征求意见

近年来网络安全事故频发&#xff0c;造成了不少损失和危害。为了减少网络安全事故的发生&#xff0c;规范网络安全事件的报告&#xff0c;国家互联网信息办公室根据《中华人民共和国网络安全法》等法律法规起草了《网络安全事件报告管理办法&#xff08;征求意见稿&#xff09;…

释放创意,点亮视频!红巨星Magic Bullet Looks带给您绚丽的色彩魔法

Red Giant Magic Bullet Looks 是一款适用于Mac的视频后期处理软件。它是由Red Giant公司开发的一款专业级颜色校正和调色工具&#xff0c;旨在帮助电影制作人、视频编辑和摄影师实现令人惊叹的视觉效果。 Magic Bullet Looks 提供了一个直观而强大的用户界面&#xff0c;使用…

Linux操作系统(Crontab计划任务+NTP时间同步服务器)

如何修改linux系统时间 与时间相关的命令&#xff0c;查看当前的时间 运行 date 即可 cal 查看当前月份的日历 运行 timedatectl 查看时间详细参数 &#xff08; NTP&#xff1a; network time protocol 网络时间协议 &#xff09; &#xff08; local time : 本地时间 &#x…

大创项目推荐 深度学习LSTM新冠数据预测

文章目录 0 前言1 课题简介2 预测算法2.1 Logistic回归模型2.2 基于动力学SEIR模型改进的SEITR模型2.3 LSTM神经网络模型 3 预测效果3.1 Logistic回归模型3.2 SEITR模型3.3 LSTM神经网络模型 4 结论5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 …

智能优化算法应用:基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鱼鹰算法4.实验参数设定5.算法结果6.参考文献7.MA…

6、IDEA集成GitHub/码云

这里写目录标题 1、设置GitHub账号2、分享工程到GitHub3、Push推送本地库到远程库4、Pull拉取远程库到本地库5、Clone克隆远程库到本地6、码云简介 1、设置GitHub账号 可以采用两种登录方式&#xff1a;账户密码登入、口令登入。 2、分享工程到GitHub 先在GitHub中创建一个远…

【Hive_05】企业调优1(资源配置、explain、join优化)

1、 计算资源配置1.1 Yarn资源配置1.2 MapReduce资源配置 2、 Explain查看执行计划&#xff08;重点&#xff09;2.1 Explain执行计划概述2.2 基本语法2.3 案例实操 3、分组聚合优化3.1 优化说明&#xff08;1&#xff09;map-side 聚合相关的参数 3.2 优化案例 4、join优化4.1…

ARCGIS PRO SDK GeometryEngine处理独立几何图形的函数

1、面积类&#xff1a;pol为Polygon 1).Area&#xff1a;获取几何图形的面积。这是使用二维笛卡尔数学来计算面积的平面测量 double d GeometryEngine.Instance.Area(pol) 2).GeodesicArea:获取几何图形的椭球面积 …

highcharts的甘特图设置滚动时表头固定,让其他内容跟随滚动

效果图&#xff1a;最左侧的分类列是跟随甘特图滚动的&#xff0c;因为这一列如果需要自定义&#xff0c;比如表格的话可能会存在行合并的情况&#xff0c;这个时候甘特图是没有办法做的&#xff0c;然后甘特图的表头又需要做滚动时固定&#xff0c;所以设置了甘特图滚动时&…