Redis高级数据类型-HyperLogLogBitmap以及使用两种数据类型完成网站数据统计

news2024/11/27 2:19:43

在这里插入图片描述

网站数据统计

在这里插入图片描述

定义相关的Redis Key

    /**
     * 单日UV
     */
    public static String getUVKey(String date) {
        return PREFIX_UV+SPLIT+date;
    }

    /**
     * 记录区间UV
     * @param startData 开始日期
     * @param endDate 结束日期
     * @return
     */
    public static String getUVkey(String startData,String endDate){
        return PREFIX_UV+SPLIT+startData+SPLIT+endDate;
    }

    /**
     * 单日活跃用户
     * @param date
     * @return
     */
    public static String getDAUkey(String date){
        return PREFIX_DAU+SPLIT+date;
    }

    /**
     * 区间活跃用户
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @return
     */
    public static String getDAUKey(String startDate,String endDate){
        return PREFIX_DAU+SPLIT+startDate+SPLIT+endDate;
    }

定义DataService

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.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Service
public class DataService {
    @Autowired
    private RedisTemplate redisTemplate;
    //定义日期格式
    private SimpleDateFormat df=new SimpleDateFormat("yyyyMMdd");
    //将指定的IP计入UV
    public void recordUV(String ip){
        //获取redisKey
        String redisKey = RedisKeyUtil.getUVKey(df.format(new Date()));
        //存入ip
        redisTemplate.opsForHyperLogLog().add(redisKey,ip);
    }
    //统计指定日期范围内的UV
    public long calculateUV(Date start,Date end){
        //参数判空
        if(start ==null || end == null){
            throw new IllegalArgumentException("参数不能为空");
        }
        /**
         * 整理改时间范围内的key
         */
        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);
    }
    //将指定用户计入DAU
    public void recordDAU(int userId){
        String redisKey =RedisKeyUtil.getDAUkey(df.format(new Date()));
        redisTemplate.opsForValue().setBit(redisKey,userId,true);
    }
    //统计指定日期范围内的DAU
    //每一天的统计结果做一个或运算
    public long calculateDAU(Date start,Date end){
        //参数判空
        if(start ==null || end == null){
            throw new IllegalArgumentException("参数不能为空");
        }
        /**
         * 整理改时间范围内的key
         */
        List<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运算
         * 使用redis底层的链接调用or运算
         */
        return (long) redisTemplate.execute(new RedisCallback() {
            @Override
            public 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());
            }
        });

    }
}

定义拦截器


@Component
public class DataInterceptor implements HandlerInterceptor {
    @Autowired
    private DataService dataService;
    @Autowired
    private HostHolder hostHolder;
    //在请求之初统计数据
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception{
        //统计UV
        String ip= request.getRemoteHost();
        dataService.recordUV(ip);
        //统计DAU
        User user = hostHolder.getUser();
        if(user!=null){
            dataService.recordDAU(user.getId());
        }
        return true;
    }
}

定义Controller

@Controller
public class DataController {
    @Autowired
    private DataService dataService;

    /**
     * 统计页面的函数
     * @return
     */
    @RequestMapping(path = "/data",method = {RequestMethod.GET,RequestMethod.POST})
    public String getDataPage(){
        return "/site/admin/data";
    }

    /**
     * 统计网站UV
     * @param start
     * @param end
     * @param model
     * @return
     */
    @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);
        //将处理结果发给/data进行另一半的处理
        return "forward:/data";
//        return "/site/admin/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);
        //将处理结果发给/data进行另一半的处理
        return "forward:/data";
//        return "/site/admin/data";
    }
}

添加权限

.antMatchers(
                        "/discuss/delete",
                        "/data/**"
                )

页面示例

<div class="main">
			<!-- 网站UV -->
			<div class="container pl-5 pr-5 pt-3 pb-3 mt-3">
				<h6 class="mt-3"><b class="square"></b> 网站 UV</h6>
				<form class="form-inline mt-3" method="post" th:action="@{/data/uv}">
					<input type="date" class="form-control" required name="start" th:value="${#dates.format(uvStartDate,'yyyy-MM-dd')}"/>
					<input type="date" class="form-control ml-3" required name="end" th:value="${#dates.format(uvEndDate,'yyyy-MM-dd')}"/>
					<button type="submit" class="btn btn-primary ml-3">开始统计</button>
				</form>
				<ul class="list-group mt-3 mb-3">
					<li class="list-group-item d-flex justify-content-between align-items-center">
						统计结果
						<span class="badge badge-primary badge-danger font-size-14" th:text="${uvResult}">0</span>
					</li>
				</ul>
			</div>
			<!-- 活跃用户 -->
			<div class="container pl-5 pr-5 pt-3 pb-3 mt-4">
				<h6 class="mt-3"><b class="square"></b> 活跃用户</h6>
				<form class="form-inline mt-3"method="post" th:action="@{/data/dau}">
					<input type="date" class="form-control" required name="start" th:value="${#dates.format(dauStartDate,'yyyy-MM-dd')}"/>
					<input type="date" class="form-control ml-3" required name="end" th:value="${#dates.format(dauEndDate,'yyyy-MM-dd')}"/>
					<button type="submit" class="btn btn-primary ml-3">开始统计</button>
				</form>
				<ul class="list-group mt-3 mb-3">
					<li class="list-group-item d-flex justify-content-between align-items-center">
						统计结果
						<span class="badge badge-primary badge-danger font-size-14" th:text="${dauResult}">0</span>
					</li>
				</ul>
			</div>				
		</div>

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

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

相关文章

05 行列式

行列式 面积变化行列式空间定向改变三维空间行列式的计算 这是关于3Blue1Brown "线性代数的本质"的学习笔记。 面积变化 线性变换会使得基向量 i ⃗ \vec{i} i 和 j ⃗ \vec{j} j ​围城的区域面积被缩放。 图1 线性变换可能会使得基向量 i ⃗ \vec{i} i 和 j ⃗ …

webJS基础-----制作一个时间倒计时

1&#xff0c;可以使用以下两个方式制作 方式1&#xff1a;setTimeout ()定时器是在指定的时间后执行某些代码&#xff0c;代码执行一次就会自动停止&#xff1b; 方式2&#xff1a;setInterval ()定时器是按照指定的周期来重复执行某些代码&#xff0c;该定时器不会自动停止…

从 LLM 大模型到 AI Agent 技术演进

▼最近直播超级多&#xff0c;预约保你有收获 近期直播&#xff1a;《基于 LLM 大模型的微调构建AI Agents 案例实践》 —1— LLM 大模型有哪些局限性&#xff1f; 给定一些字或者词&#xff08;称为 token&#xff09;&#xff0c;预测下一个字或者词模型&#xff0c;就是语言…

基于Magma构建灵活、低成本无线接入网

传统蜂窝网络一般基于特定接入技术并针对大规模公共网络设计&#xff0c;无法灵活适配小规模网络以及异构无线技术。本文介绍了Magma在构建低成本异构无线接入方面的探索。原文: Building Flexible, Low-Cost Wireless Access Networks With Magma 摘要 当今仍然有数十亿人受限…

想学计算机编程从什么学起?零基础如何自学计算机编程?中文编程开发语言工具箱之渐变标签组构件

想学计算机编程从什么学起&#xff1f;零基础如何自学计算机编程&#xff1f; 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#xff0c;而且可以开发大型的软件&#xff0c;…

跨平台联调代码:Windows下VS2022远程连接Linux-protobuf为例

文章目录 Linux上头文件的位置Linux上共享库的位置Linux上配置好环境变量Windows上VS上的设置添加包含目录与库目录设置链接参数-库依赖项设置编译参数更新远程标头管理器代码的书写输出 Linux上头文件的位置 Linux上我的protobuf头文件的位置为&#xff1a; /usr/local/prot…

2023年中国制糖行业研究报告

第一章 行业概况 1.1 定义 制糖行业是指以甘蔗、甜菜等为主要原料&#xff0c;通过一系列的工艺流程&#xff0c;生产糖以及相关副产品的产业。它是食品工业的重要组成部分&#xff0c;为人们日常生活中的甜蜜体验提供了必不可少的物质基础。 主要原料&#xff1a; 制糖行业…

深入剖析:正则表达式的奥秘

简介 正则表达式&#xff08;Regular Expressions&#xff09;是一种强大的文本处理工具&#xff0c;一种用于匹配文本模式的字符串。它由特定的字符和操作符组成&#xff0c;用于定义一个搜索模式。这些搜索模式可以用于文本搜索、替换、验证和提取数据等多种用途。 以下是一…

在搜索引擎中屏蔽csdn

csdn是一个很好的技术博客&#xff0c;里面信息很丰富&#xff0c;我也喜欢在csdn上做技术笔记。 但是CSDN体量太大&#xff0c;文章质量良莠不齐。当在搜索引擎搜索技术问题时&#xff0c;搜索结果中CSDN的内容占比太多&#xff0c;导致难以从其他优秀的博客平台中获取信息。因…

Mac安装VMware

去官网下载一下VMware Download VMware Fusion | VMware | SG 下载完成之后&#xff0c;打开直接闪退&#xff0c;参考这篇文章解决 解决macOS13安装Fusion13闪退的问题-CSDN博客 然后即可成功顺行

linux入门到地狱

linux—001入门 IT圈必备(前端工作者用的比较少) 老旧电脑跑linux不容易卡 我代码没保存windows闪退&#xff0c;僵停(vs2019卡掉线)&#xff0c;重启更新,占用cpu内存服务报错pip各种bug 出来生态环境友好其他的全是bug(bug时间成本超过了windows快捷友好生态) 那就说明wind…

行业安卓主板-基于RK3568/3288/3588的AI视觉秤/云相框/点餐机/明厨亮灶行业解决方案(一)

AI视觉秤 单屏Al秤集成独立NPU&#xff0c;可达0.8Tops算力&#xff0c;令AI运算效率大幅提升&#xff0c;以实现生鲜商品快速准确识别&#xff0c;快速称重打印标签&#xff0c;降低生鲜门店运营成本&#xff0c;缓解高峰期称重排队拥堵的现象&#xff0c;提高称重效率&#…

1、Sentinel基本应用限流规则(1)

Sentinel基本应用&限流规则 1.1 概述与作用 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。缓存、降级和限流是保护微服务系统运行稳定性的三大利器。 缓存&#xff1a;提升系统访问速度和增大系统能处理的容量 降级&#xff1a;当服务出问题或者影…

如何写复盘报告

复盘报告在it公司中是为了在出现事情后&#xff0c;我们更好的回顾事情的前因后果&#xff0c;定位问题&#xff0c;指定解决措施&#xff0c;并且宣导&#xff0c;让这类事情减少发生的概率。那复盘报告一般怎样写合适呢&#xff1f;下来我们就看看&#xff0c; 一、一般会先…

Elasticsearch:RAG vs Fine-tunning (大语言模型微调)

如果你对 RAG 还不是很熟悉的话&#xff0c;请阅读之前的文章 “Elasticsearch&#xff1a;什么是检索增强生成 - RAG&#xff1f;”。你可以阅读文章 “Elasticsearch&#xff1a;在你的数据上训练大型语言模型 (LLM)” 来了解更多关于如何训练你的模型。在今天的文章中&#…

Linux项目自动化构建工具-make/Makefile使用

make/Makefile使用介绍 make是一个命令makefile是一个在当前目录下存在的一个具有特定格式的文本文件 ​ 下面我们设计一个场景&#xff0c;实现make命令对我们code.c文件进行编译和删除。 1 #include<stdio.h> 2 3 int main() 4 { 5 printf("hello,world!…

账户权限控制

1.首先配置一个单群组4节点的链 1.1创建操作目录 cd ~ && mkdir -p fisco && cd fisco 1.2下载国内脚本 curl -#LO https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/FISCO-BCOS/FISCO-BCOS/releases/v2.9.1/build_chain.sh && chmod ux bu…

Python算法例7 四数乘积

1. 问题描述 给定一个长度为n的数组a和一个正整数k&#xff0c;从数组中选择四个数&#xff0c;要求四个数的乘积小于等于k&#xff0c;求方案总数。 2. 问题示例 给定n5&#xff0c;a[1&#xff0c;1&#xff0c;1&#xff0c;2&#xff0c;2]&#xff0c;k3&#xff0c;返…

R语言 复习 习题图片

这是日天土申哥不知道从哪淘来的R语言复习知识点图片&#xff0c;大部分内容都是课后习题的答案 加油吧&#xff0c;骚年&#xff0c;考个好分数

【重学C++基础知识笔记】详细版

基础 常量 C++ 中有两种简单的定义常量的方法: 使用#define,如:#define PI 3.1415926;使用const, 如:const double PI = 3.1415926;注明: 尽量使用const定义变量,#define不会出现在编译器期 #define ASPECT RATIO 1.653 // 在编译时出错,很难排错const doubl…