redis-缓存设计-统计1秒 5秒 1分钟 访问数量

记录统计

主要是通过精度算出时间各个时间片的开始时间 作为hash 相同时间片开始时间是一致的 天统计 时间片都是从日期的早8点开始

复制代码
 /**
     * 毫秒为单位 统计1秒 5秒 1分钟 1小时 5小时 1天的统计信息
     */
   static Integer[] preisions = new Integer[]{1000, 5000, 60000, 300000, 3600000, 18000000, 86400000};

    public static void updateCounter(Jedis conn, int productId, int count) {
        Long currentDate = System.currentTimeMillis();
        for (int i = 0; i < preisions.length; i++) {
            Integer index = preisions[i];
            //算出指定时间维度的开始时间片
            Long startDate = (Long) (currentDate / index) * index;
            String hash = "product:" + productId;
            //指定时间片的精度+1
            conn.hincrBy(hash, startDate.toString(), count);
            //将清理的key加入到一个回收的set 存储key和精度
            conn.zadd("recovery", 0, String.format("%s_%s_%s", hash, index, startDate));
        }
    }
复制代码

获取统计

通过精度算出开始时间时间片 然后再hash获取统计信息

复制代码
 /**
     * 获得指定精度的统计数量
     * @param conn
     * @param productId
     * @param preisions
     * @return
     */
    public static String getCounter(Jedis conn,int productId,Integer preisions){
        Long startDate = (Long) (System.currentTimeMillis() / preisions) * preisions;
        String hash = "product:" + productId;
        return conn.hget(hash,startDate.toString());
    }
复制代码

数据清理

随着时间的增长 hash时间片会越来越多,清理老的时间片

复制代码
   /**
     * 这里应该使用管道,因为方便打印日志 所以没有使用管道
     * @param conn
     */
    public static void clearCounter(Jedis conn)  {
        new Thread(new Runnable() {
            @Override
            public void run() {
                String recoveryKey = "recovery";
                int index = 0;
                while (true) {
                    if (conn.zcard("recovery") <= 0) {
                        try {
                            Thread.sleep(1000);//没有可回收的时候直接等待 休息一会儿
                            continue;
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //每次检查回收50个
                    Set<String> hashs = conn.zrange(recoveryKey, 0, 50);
                    for (String hash :
                            hashs) {
                        String[] hashArray = hash.split("_");
                        int preision = Integer.valueOf(hashArray[1]);//取得精度
                        Long startDate = Long.valueOf(hashArray[2]);
                        String productKey = hashArray[0];//取得数据hash key
                        //开始时间加上精度 如果小于当前时间 表示时间片过了 执行删除
                        final Calendar calendar = Calendar.getInstance();
                        calendar.setTime(new Date(startDate));
                        calendar.add(Calendar.MILLISECOND, preision);
                        //在时间片之内
                        if (calendar.getTimeInMillis() > System.currentTimeMillis()) {
                            continue;
                        } else {
                            //执行删除
                            Long result = conn.hdel(productKey, startDate.toString());
                            conn.zrem(recoveryKey,hash);
                            System.out.println(String.format("移除了key:%s,过期数据%s,删除结果:%s", productKey, startDate.toString(), result));
                        }
                    }
                }
            }
        }).start();
    }
复制代码

main方法

复制代码
  public static void main(String[] args)
            throws Exception {
        Jedis conn = new Jedis("127.0.0.1", 6379);
        Jedis conn2 = new Jedis("127.0.0.1", 6379);
        Jedis conn3 = new Jedis("127.0.0.1", 6379);
        conn.flushDB();
        //启动清理器
        clearCounter(conn2);
        for(int i=0;i<65;i++){
            Thread.sleep(1000);
            updateCounter(conn,1,1);
        }
        //获得 1分钟的统计数量
       System.out.println("一分钟的统计数量:"+getCounter(conn3,1,60000));

    }
复制代码

打印

真实案例

文章访问数量,

某个通过时间片为key 某个时间范围内的都incr 累加 

然后写入list或set

后续时间片过了 再将这个key的数值写入到数据库 避免数据库每次访问都i++

 

posted @   意犹未尽  阅读(1852)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
历史上的今天:
2018-07-23 Zookeeper-单机/集群安装
点击右上角即可分享
微信分享提示