Appearance
当查询QPS过大时,此时定时器更新redis先删除再添加就会出现缓存击穿的问题。就必定导致大量的请求都打到数据库上面,从而把数据库打垮,这就出现了缓存击穿问题,那么该如何解决呢?
TIP
在高并发系统中,大量的请求同时查询一个key时,如果这个key正好失效或删除,就会导致大量的查询都打到数据库上,这就被称为缓存击穿。
针对这种定时更新缓存的特定场景,解决方案:采用主从轮询
- 定时更新:开辟2块缓存A和B,定时器在更新缓存的时候,先更新B缓存然后在更新A缓存,记得要按这个顺序来。
- 查询:用户先查询缓存A,如果缓存A查询不到(例如,更新缓存的时候删除了),再查询缓存B
淘宝聚划算的缓存击穿实现
java
@PostConstruct
public void initJHSAB(){
log.info("启动AB定时器..........");
new Thread(()->runJhsAB()).start();
}
java
public void runJhsAB() {
while (true){
//模拟从数据库读取100件 特价商品,用于加载到聚划算页面
List<Product> list=this.products();
//先更新B
this.redisTemplate.delete(Constants.JHS_KEY_B);
this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY_B,list);
//再更新A
this.redisTemplate.delete(Constants.JHS_KEY_A);
this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY_A,list);
try {
Thread.sleep(1000*60);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("重新刷新..............");
}
}
java
@GetMapping(value = "/findAB")
public List<Product> findAB(int page, int size) {
List<Product> list=null;
long start = (page - 1) * size;
long end = start + size - 1;
try {
//采用redis,list数据结构的lrange命令实现分页查询。
list = this.redisTemplate.opsForList().range(Constants.JHS_KEY_A, start, end);
//用户先查询缓存A,如果缓存A查询不到(例如,更新缓存的时候删除了),再查下缓存B
if (CollectionUtils.isEmpty(list)) {
this.redisTemplate.opsForList().range(Constants.JHS_KEY_B, start, end);
}
log.info("{}", list);
} catch (Exception ex) {
//这里的异常,一般是redis瘫痪 ,或 redis网络timeout
log.error("exception:", ex);
//TODO 走DB查询
}
return list;
}