在上节我们已经分析了缓存更新策略的最佳实践,因此这节课我们就动手来试试,给查询商铺的缓存添加超时剔除和主动更新的策略。
修改ShopController中的业务逻辑,满足下面的需求,需求分为两步,也就是我们查询和更新时要干的事:
① 根据id查询店铺时,如果缓存未命中,则查询数据库,将数据库结果写入缓存,并设置超时时间,这样将来就可以做到超时剔除了
② 根据id修改店铺时,先修改数据库,再删除缓存
文章目录
一、查询
RedisConstants.java
public static final Long CACHE_SHOP_TTL = 30L;
ShopServiceImpl.java
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public Result queryById(Long id) {
String key = RedisConstants.CACHE_SHOP_KEY + id;
String shopJson = stringRedisTemplate.opsForValue().get(key);
// 2.判断是否存在
if (StrUtil.isNotBlank(shopJson)) {
// 3.存在,直接返回
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return Result.ok(shop);
}
// 4.不存在,根据id查询数据库
Shop shop = getById(id);
// 5.查询数据库不存在,直接返回错误
if (shop == null) {
return Result.fail("店铺不存在!");
}
// 6.存在,写入redis
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// 7.返回
return Result.ok(shop);
}
}
二、更新
ShopController.java
之前的更新是直接使用Mybatis-Plus的 updateById()
,因此没有我们什么业务,现在我们要将它改一下。
我们会在service层添加缓存逻辑
/**
* 更新商铺信息
* @param shop 商铺数据
* @return 无
*/
@PutMapping
public Result updateShop(@RequestBody Shop shop) {
// 写入数据库
return shopService.update(shop);
}
IShopService.java
Result update(Shop shop);
ShopServiceImpl.java
更新我们分析过,第一步就是更新数据库,第二步就是删除缓存,最后返回结果即可。
其中删除的key就是之前存储时用到的key
// 将来删除缓存的时候如果抛异常,那么更新数据库的也应该回滚,因此整个方法应该有一个统一的事物
@Transactional
@Override
public Result update(Shop shop) {
Long id = shop.getId();
if (id == null) {
return Result.fail("店铺id不能为空");
}
// 1.更新数据库
updateById(shop);
// 2.删除缓存
stringRedisTemplate.delete(RedisConstants.CACHE_SHOP_KEY + id);
return Result.ok();
}
此时我们就实现了缓存的同步更新,因为我们现在是一个单体项目,因此数据库操作和缓存操作都在一个方法里,可以通过事物去控制他们的原子性。
但如果你是一个分布式系统,那么这块可能就麻烦一点了,有可能你删除完数据库,删除缓存这块的动作可能不是由你来做的,而是另外一个系统,有可能是通过MQ去异步的通知对方,然后对方去完成这个缓存的处理。当然如果你想要保证两边的这样的一个一致性,就必须借助于我们之前给大家讲的TCC这样的方案才能保证这种一致性。
三、测试
重启服务,清除控制台数据,然后到前端页面随便查询一个店铺,查询的时候记得清除之前查询店铺时留下的缓存
此时redis中已经没有数据了,我们重新访问店铺,刷新,此时第一次访问,因为缓存未命中,因此是会到数据库中查询店铺信息的
接下来验证写入redis有没有加过期时间,TTL:1645S,证明它已经有过期时间了。
证明定时清理这块已经触发了。
接下来就是测试更新,看看更新后缓冲是否也会跟着去更新。
因为更新的动作一般是由管理端去做的,现在我们浏览器看到的页面是面向用户的端,而不是商家的后台管理,因此我们并没有像面向客户的端的页面操作,此时我们只能借助于像Postman这样的工具去实现。
将103茶餐厅
改为了102茶餐厅
可以发现请求成功
回到控制台来查看结果,可以发现更新操作确实是执行了
执行了后我们去数据库中做一个校验:可以发现名字确实变成102茶餐厅
了。
回到redis刷新查看,可以发现原来的缓存已经删掉了。我们当初的策略就是更新的时候移除缓存,因此此时如果有用户在浏览器中再次刷新,它看到的一定是 102茶餐厅
并且此时应该会重建缓存,值应该也为 102茶餐厅
,所以我们现在已经实现了数据库
因此现在我们已经实现了数据库与缓存的数据同步了,每当数据库发生更新,缓存会移除,这样一来用户查询就能查到最新的数据了。