需求:修改根据id查询商铺的业务,基于逻辑过期方式来解决缓存击穿问题。

- 使用逻辑过期改造queryById()方法,解决缓存击穿
java
/**
* 根据id查询数据。
*
* 作用:
* 1.逻辑过期解决缓存击穿;
*
* @param id业务id
* @return处理结果
*/
@Override
public Result queryById(Long id) {
//逻辑过期解决缓存击穿
Shop shop=queryWithLogicalExpire(id);
if(shop == null){
return Result.fail("店铺不存在");
}
return Result.ok(shop);
}
/**
* 使用逻辑过期解决缓存击穿查询。
*
* 作用:
* 1.从Redis查询商铺缓存;
* 2.判断是否存在;
* 3.不存在,直接返回;
* 4.命中,需要先把Json反序列化为对象;
* 5.判断是否过期;
*
* @param id业务id
* @return处理结果
*/
public Shop queryWithLogicalExpire(Long id){
String key= CACHE_SHOP_KEY+id;
//1.从Redis查询商铺缓存
String shopJson=stringRedisTemplate.opsForValue().get(key);
//2.判断是否存在
if(StrUtil.isBlank(shopJson)){
//3.不存在,直接返回
return null;
}
//4.命中,需要先把Json反序列化为对象
RedisData redisData=JSONUtil.toBean(shopJson,RedisData.class);
JSONObject data=(JSONObject) redisData.getData();
Shop shop=JSONUtil.toBean(data,Shop.class);
LocalDateTime expireTime=redisData.getExpireTime();
//5.判断是否过期
if(expireTime.isAfter(LocalDateTime.now())){
//5.1.未过期,直接返回店铺信息
return shop;
}
//5.2.已过期,需要缓存重建
//6.缓存重建
//6.1.获取互斥锁
String lockKey=LOCK_SHOP_KEY+id;
boolean isLock=tryLock(lockKey);
//6.2.判断是否获取锁成功
if(isLock){
//6.3.成功,开启独立线程,实现缓存重建
CACHE_REBUILD_EXECUTOR.submit(()->{
try {
//重建缓存
this.saveShop2Redis(id,20L);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//释放锁
unlock(lockKey);
}
});
}
//6.4.返回过期的商铺信息
return shop;
}
/**
* 执行saveShop2Redis相关业务逻辑。
*
* 作用:
* 1.查询店铺数据;
* 2.封装逻辑过期时间;
* 3.写入Redis;
*
* @param id业务id
* @param expireSeconds expireSeconds参数
* @return无返回值
*/
public void saveShop2Redis(Long id,Long expireSeconds) throws InterruptedException {
//1.查询店铺数据
Shop shop=getById(id);
Thread.sleep(200);
//2.封装逻辑过期时间
RedisData redisData=new RedisData(); //如果直接把Shop存进Redis,里面是没有“过期时间”这个字段的,不能为了存Redis,去修改数据库表对应的实体类
redisData.setData(shop);
redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds)); //以现在的系统时间为起点,往后推 expireSeconds 秒
//3.写入Redis
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));
}测试: 
先对缓存进行热点数据预热,预热数据的逻辑过期时间设置了10秒,所以等待10秒缓存逻辑过期后,在数据库中修改id为1的店铺数据(热点数据),使用Jmeter进行压力测试(可以将重建缓存的过程延迟1秒钟,让效果更加明显),第一次查询结果还是旧数据,1秒左右以后返回的缓存数据发生了更新(因为此时缓存已经重建好了),并且访问数据库只重建了一次缓存,验证了并发安全问题。

