redis-緩存設計-統計1秒 5秒 1分鍾 訪問數量


 

 

轉載自:

    https://www.cnblogs.com/LQBlog/p/13365191.html

 

 

背景:

    某項目做完后,對於系統內部的監控狀況幾乎為零,造成了后期處理問題的難度大幅增加,為了實現項目框架從黑盒到白盒的轉變,針對目前情況做了兩個重大的結構轉變。

    1、對日志的格式進行重定義,使日志能夠以機器的形式閱讀,從中讀取信息,並反映到框架圖中

    2、針對數據流,從頭到尾進行流程追蹤,同時監控數據流在每個業務點上的統計值。

    以下內容,就是針對數據統計而做的。

 

 redis-緩存設計-統計1秒 5秒 1分鍾 訪問數量

    以下代碼采用JAVA編寫

    實現要點:

    1、采用ZSET集合統計值。采用KEY-VALUE會造成多個KEY,且也不易於刪除,ZSET可以使用zrembyscore刪除,即不會產生遺漏,也方便刪除全部內容。

    2、用時間戳取整 * 間隔  方式自動生成ZSET的KEY。

    3、ZSET的hincrby是原子性操作(類似的INCR/DECR也是原子性操作),能效上加速實現。KEY是監控目標,SCORE是每秒,每分鍾,每小時的訪問量

  

文章主目錄


    記錄統計
    獲取統計
    數據清理
    main方法

 

記錄統計


主要是通過精度算出時間各個時間片的開始時間 作為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));

    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM