需求
1.实现任意数据行的可以设计不同的延迟周期进行刷新或者同步任务
2.最热的2000个商品缓存
自动延迟调度
加入调度列表
/**
* 将需要主动更新的的数据加入自动调度列表
* @param conn
* @param row_id
* @param delay
*/
public static void scheduleRowCache(Jedis conn,String row_id,int delay){
//记录此id周期多少秒执行一次 刷新
conn.zadd("delay:",delay,row_id);
//加入定时执行列表 并设置立即执行
conn.zadd("schedule:",System.currentTimeMillis(),row_id);
}
调度方法
public static void run(Jedis conn){
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
//获取需要立即执行的列表
Set<String> ids = conn.zrangeByScore("schedule:", 0, System.currentTimeMillis());
//没有则释放时间片 同时暂停500毫秒
if (ids == null || ids.size() <= 0) {
Thread.yield();
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (String id :
ids) {
System.out.println(id + "执行了数据刷新");
//获得此数据延迟时间
Double delay = conn.zscore("delay:", id);
//重新加入延迟队列
//加入定时执行列表 并设置立即执行
conn.zadd("schedule:", System.currentTimeMillis() + (delay * 1000), id);
}
}
}
}).start();;
}
测试
public static final void main(String[] args)
throws Exception {
Jedis conn = new Jedis("127.0.0.1", 6379);
conn.select(15);
//5秒刷新一次
scheduleRowCache(conn,"1",5);
run(conn);
Thread.sleep(1000000);
}
最热商品缓存
访问商品
/**
* 更新rank
* @param conn
* @param productId
* @return
* @throws InterruptedException
*/
public static String viewProduct(Jedis conn,String productId) throws InterruptedException {
//更新评分
conn.zincrby("viewed:",-1,productId);
//获得排名
double rank= conn.zrank("viewed:",productId);
String html=null;
//如果<=2000表示走缓存
if(rank<=2000){
//先查缓存
html= conn.get("product:"+productId);
if(html==null){
html=select(productId);
conn.set("product:"+productId,html);
}
}else{
html =select(productId);
}
return html;
}
/**
* 模拟db 和生成html
* @return
* @throws InterruptedException
*/
public static String select(String productId) throws InterruptedException {
Thread.sleep(2000);
return String.format("<html>我是商品%s的商品详情html</html>",productId);
}
定时任务清除非热点数据
/**
* 此方法应该定时任务5秒调用一次
* @param conn
*/
public static void clearCache(Jedis conn){
conn.zremrangeByRank("viewed:",0,-2001);
//将所有商品的浏览数量降低一半 未测试通过,,,不造咋减半的 书上py是这样写
conn.zinterstore("viewed:","{'viewed:':0.5");
}
2020-08-21:已解决 通过 WEIGHTS 设置乘法因子 默认是1 我们设置0.5 就会在原有的基础上* 0.5
参考:http://doc.redisfans.com/sorted_set/zunionstore.html#zunionstore
测试
public static void main(String[] args)
throws Exception {
Jedis conn = new Jedis("127.0.0.1", 6379);
conn.select(15);
viewProduct(conn,"3");
viewProduct(conn,"3");
viewProduct(conn,"3");
viewProduct(conn,"3");
viewProduct(conn,"3");
viewProduct(conn,"3");
//清空2000排名以外的 同时重置2000以内的排名 conn.zscore("viewed:","3")
clearCache(conn);
Thread.sleep(1000000);
}
实际使用例子
比如我们线上几千家门店,用户进入门店首页,为了追求性能会在redis增加缓存,但是每个门店都按照传统的 设置缓存加失效时间那么redis内存消耗会很大。
实际中有些偏远的门店,用户数会很少,所以统计最热门店,使用定时任务每10分钟剔除缓存 然后将最热门店的程序主动刷新。而不那么热门的门店则不使用缓存