Skip to content

当查询QPS过大时,此时定时器更新redis先删除再添加就会出现缓存击穿的问题。就必定导致大量的请求都打到数据库上面,从而把数据库打垮,这就出现了缓存击穿问题,那么该如何解决呢?

TIP

在高并发系统中,大量的请求同时查询一个key时,如果这个key正好失效或删除,就会导致大量的查询都打到数据库上,这就被称为缓存击穿。

针对这种定时更新缓存的特定场景,解决方案:采用主从轮询

  1. 定时更新:开辟2块缓存A和B,定时器在更新缓存的时候,先更新B缓存然后在更新A缓存,记得要按这个顺序来。
  2. 查询:用户先查询缓存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;
}