> 技术文档 > SpringBoot整合Redis:面试必考题-缓存击穿--逻辑过期解决_springboot redis面试题

SpringBoot整合Redis:面试必考题-缓存击穿--逻辑过期解决_springboot redis面试题

🎉🎉欢迎光临,终于等到你啦🎉🎉

🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀

🌟持续更新的专栏Redis实战与进阶

本专栏讲解Redis从原理到实践

这是苏泽的个人主页可以看到我其他的内容哦👇👇

努力的苏泽SpringBoot整合Redis:面试必考题-缓存击穿--逻辑过期解决_springboot redis面试题http://suzee.blog.csdn.net


本期讲解Redis企业必考面试题  缓存击穿使用逻辑过期解决

首先要了解什么是缓存击穿可以看我这一篇:http://t.csdnimg.cn/jMAqw

那么我们复原一下业务场景

分析思路

  1. 确定数据的更新逻辑:首先,需要明确数据的更新逻辑。了解数据是如何被修改、更新或者删除的,以及这些操作是由哪些业务逻辑触发的。这可以包括数据库更新、后端服务的数据变更通知等。

  2. 监听数据的更新事件:在数据被修改、更新或者删除时,需要能够捕捉到这些事件。这可以通过数据库的触发器(Trigger)机制、消息队列、发布-订阅模式等方式来实现。目的是在数据更新时能够及时感知到。

  3. 更新缓存和设置逻辑过期时间:当接收到数据更新事件时,需要更新相应的缓存,并重新设置逻辑过期时间。这意味着需要将最新的数据加载到缓存中,并根据业务规则设置适当的过期时间。这可以通过缓存服务的API或者命令来完成。

  4. 缓存访问时的逻辑判断:在每次访问缓存之前,需要进行逻辑判断以确定数据是否过期。这可以基于缓存中存储的逻辑过期时间和当前时间进行比较。如果数据已经过期,需要重新加载最新数据到缓存中。

  5. 数据加载的并发控制:在数据过期时,可能会有多个线程同时检测到数据过期并尝试重新加载数据到缓存。为了避免并发查询对后端服务造成压力,可以使用互斥锁或其他并发控制机制,确保只有一个线程负责重新加载数据,其他线程从缓存中获取旧数据。

  6. 定期刷新数据:除了根据数据更新事件进行缓存更新外,还可以定期刷新数据来保持缓存的新鲜性。定期刷新可以通过设置一个时间间隔,在规定的时间间隔内重新加载数据到缓存中,避免数据长时间未更新而导致的过期数据。

代码实现

首先

我们先写一个RedisData的类专门封装到期日期

@Datapublic class RedisData { private LocalDateTime expireTime; private Object data;}

然后在ShopServiceImpl中写一个专门处理逻辑过期的方法saveShop2Redis

//数据预热private void saveShop2Redis(long id,long expireSeconds){ //1.查询店铺数据 Shop shop = getById(id); //2.封装逻辑过期时间 RedisData redisData = new RedisData(); redisData.setData(shop); redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds)); //3.写入Redis stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));}

然后再写一个专门处理逻辑过期的方法queryWithLogicalExpiration

//搭建一个线程池private static final ExecutorService executor = Executors.newFixedThreadPool(10);//解决缓存穿透问题public Shop queryWithLogicalExpiration(long id){ String key = CACHE_SHOP_KEY + id; //1.先查询Redis String Jsonshop = stringRedisTemplate.opsForValue().get(key); //2.判断是否存在 if (StrUtil.isBlank(Jsonshop)){ //不存在 直接返回空 return null; } //3.命中 先反序列化为对象 RedisData redisData = JSONUtil.toBean(Jsonshop, RedisData.class); Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class); //4.查询是否过期 if (redisData.getExpireTime().isAfter(LocalDateTime.now())){ //5.未过期 直接返回 return shop; } //6.过期 重构缓存 String lockKey = LOCK_SHOP_KEY + id; //6.1获得互斥锁 // 6.2判断是否成功 if(tryLock(lockKey)){ //6.3成功 开启新线程 实现重构缓存 executor.submit(() ->{ //重建缓存 try { //这里做的就是1.先查数据库2.然后写入Redis  //其实就是重构缓存 this.saveShop2Redis(id,30L); } catch (Exception e) { throw new RuntimeException(e); }finally { //关锁 unLock(lockKey); } }); } //6.4失败 直接返回旧数据 return shop;}