什么是缓存击穿
在高并发的系统中,大量的请求同时查询一个key时,如果这个key正好失效或删除,就会导致大量的请求都请求到数据库,导致数据库扛不住这种高并发流量,卡顿甚至死机。这种现象我们称为缓存击穿
如何解决缓存击穿的问题?
针对这种定时更新缓存的特定场景,解决缓存击穿一般是采用主从轮询的原理。
- 定时器更新原理
开辟2块缓存,A 和 B,定时器在更新缓存的时候,先更新B(主)缓存,然后再更新A缓存(从),记得要按这个顺序。
- 查询原理
用户先查询缓存A(从),如果缓存A查询不到(例如,更新缓存的时候删除了),再查询缓存B(主)
以上2个步骤,由原来的一块缓存,开辟出2块缓存,最终解决了缓存击穿的问题
实战:主从轮询
@PostConstruct
public void initJHSAB(){
log.info("启动AB定时器..........");
new Thread(()->runJhsAB()).start();
}
复制代码
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("重新刷新..............");
}
}
复制代码
@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;
}
复制代码
注意:主从轮询和延时双删是不一样场景,延时双删是解决双写一致性方案。
redis分布式缓存系列文章
上一章节: redis分布式缓存(二十七)一一 淘宝聚划算商品列表解决方案
- 👍🏻:有收获的,点赞鼓励!
- ❤️:收藏文章,方便回看!
- 💬:评论交流,互相进步!




近期评论