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

网站数据统计

定义相关的Redis Key

java 复制代码
    /**
     * 单日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

java 复制代码
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());
            }
        });

    }
}

定义拦截器

java 复制代码
@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

java 复制代码
@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";
    }
}

添加权限

java 复制代码
.antMatchers(
                        "/discuss/delete",
                        "/data/**"
                )

页面示例

javascript 复制代码
<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>
相关推荐
一心只为学12 分钟前
Oracle密码过期问题,设置永不过期
数据库·oracle
小光学长21 分钟前
基于vue框架的宠物销售管理系统3m9h3(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
数据库
小菜yh44 分钟前
关于Redis
java·数据库·spring boot·redis·spring·缓存
Microsoft Word1 小时前
数据库系统原理(第一章 数据库概述)
数据库·oracle
问道飞鱼1 小时前
分布式中间件-Pika一个高效的分布式缓存组件
分布式·缓存·中间件
华为云开源1 小时前
openGemini 社区人才培养计划:助力成长,培养新一代云原生数据库人才
数据库·云原生·开源
小安运维日记3 小时前
Linux云计算 |【第四阶段】NOSQL-DAY1
linux·运维·redis·sql·云计算·nosql
kejijianwen4 小时前
JdbcTemplate常用方法一览AG网页参数绑定与数据寻址实操
服务器·数据库·oracle
编程零零七4 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
高兴就好(石7 小时前
DB-GPT部署和试用
数据库·gpt