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++
标签:
redis
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
2018-07-23 Zookeeper-单机/集群安装