前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一文读懂Redis的缓存更新策略,通俗易懂

一文读懂Redis的缓存更新策略,通俗易懂

作者头像
小熊学Java
发布于 2023-07-16 06:30:53
发布于 2023-07-16 06:30:53
1.8K00
代码可运行
举报
文章被收录于专栏:全栈学习之路全栈学习之路
运行总次数:0
代码可运行

最近整理的10w字Java面试手册,涵盖了Java面试几乎都会问的面试题目

获取方式:关注公众号,回复 面试手册;

小熊学Java在线网站:https://javaxiaobear.gitee.io/

业务场景:

  • 低一致性需求:使用内存淘汰机制。例如店铺类型的查询缓存
  • 高一致性需求:主动更新,并以超时剔除作为兜底方案。例如店铺详情查询的缓存
1、主动更新
  1. Cache Aside Pattern:由缓存的调用者,在更新数据库的同时更新缓存
  2. Read/Write Through Pattern:缓存与数据库整合为一个服务,由服务维护一致性,调用者调用该服务,无需关心缓存一致性问题
  3. Write Behind Caching Pattern:调用者只操作缓存,由其他线程异步的将缓存的数据持久化到数据库,保证最终一致。

PS:一般使用方案1,由缓存的调用者,在更新数据库的同时更新缓存

2、Cache Aside Pattern

由缓存的调用者,在更新数据库的同时更新缓存

操作缓存和数据库时有三个问题需要考虑:

  1. 删除缓存还是更新缓存?
  2. 如何保证缓存与数据库的操作的同时成功或失败?
  3. 先操作缓存还是先操作数据库?
1、删除缓存还是更新缓存?

  • 更新缓存:每次更新数据库都更新缓存,无效写操作较多 ❌ 如果更新数据库的次数多,而读取的次数较少,则每次更新时,都会增加无效的更新缓存操作
  • 删除缓存:更新数据库时让缓存失效,查询时再更新缓存 ✔️
2、如何保证缓存与数据库的操作的同时成功或失败?

  • 单体系统,将缓存与数据库操作放在一个事务
  • 分布式系统,利用TCC等分布式事务方案
3、先操作缓存还是先操作数据库?

  • 先删除缓存,再操作数据库
  • 先操作数据库,再删除缓存
1、先删除缓存,再操作数据库

不存在线程安全问题场景

  1. 线程1收到请求后,先删除缓存,然后更新数据库值为20
  2. 线程2收到请求后,先查询缓存,并没有命中,则查询数据库的值为20,再写入缓存

存在线程安全问题场景

  1. 线程1收到请求后,先删除缓存
  2. 线程2收到查询请求,查询缓存发现没有命中,则去查询数据库的值为10
  3. 线程2将数据库的值写入缓存
  4. 线程1更新数据库的值为20,此时导致数据不一致问题
2、先操作数据库,再删除缓存✔️

不存在线程安全问题场景

  1. 线程2收到请求后更新数据库值为20
  2. 线程2更新完成后,删除缓存
  3. 线程1收到请求后,查询缓存,未命中,则查询数据库
  4. 线程1查询到数据后,写入缓存

存在线程安全问题场景

  1. 线程1收到查询请求后,查询缓存,未命中,则去查询数据库值为10
  2. 线程2收到更新请求,更新数据库值为20
  3. 线程2更新数据库后,删除缓存
  4. 线程1写入缓存值为10,此时缓存写入的是旧数据
3、总结

缓存更新策略的最佳实践方案:

  1. 低一致性需求:使用Redis自带的内存淘汰机制
  2. 高一致性需求:主动更新,并以超时剔除作为兜底方案
    • 先写数据库,然后再删除缓存
    • 要确保数据库与缓存操作的原子性
    • 缓存命中则直接返回
    • 缓存未命中则查询数据库,并写入缓存,设定超时时间
    • 读操作
    • 写操作
4、实战
1、查询设置超时时间
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public Result queryById(Long id) {
   //从redis查询缓存
   String key = RedisConstants.CACHE_SHOP_KEY + id;
   String shopInfo = stringRedisTemplate.opsForValue().get(key);
   //判断是否存在
   if (StrUtil.isNotBlank(shopInfo)){
       //存在则返回
       Shop shop = JSONUtil.toBean(shopInfo, Shop.class);
       return Result.ok(shop);
  }

   //不存在,则查询数据库
   Shop shop = getById(id);
   //不存在则返回错误
   if (null == shop){
       return Result.fail("店铺不存在");
  }

   //写入缓存 设置超时时间为30min
   stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
   //数据库存在,则返回

   return Result.ok(shop);
}
2、更新时删除缓存
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
@Transactional
public Result update(Shop shop) {
   Long id = shop.getId();
   if (null == id){
       return Result.fail("店铺id不能为空");
  }
   //1.更新数据库
   updateById(shop);
   //2.删除缓存
   stringRedisTemplate.delete(RedisConstants.CACHE_SHOP_KEY + shop.getId());

   return Result.ok();
}

如果觉得内容不错的话,希望大家可以帮忙点赞转发一波,这是对我最大的鼓励,感谢🙏🏻

END

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-10-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小熊学Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 最近整理的10w字Java面试手册,涵盖了Java面试几乎都会问的面试题目
    • 1、主动更新
    • 2、Cache Aside Pattern
      • 1、删除缓存还是更新缓存?
      • 2、如何保证缓存与数据库的操作的同时成功或失败?
      • 3、先操作缓存还是先操作数据库?
    • 3、总结
    • 4、实战
      • 1、查询设置超时时间
      • 2、更新时删除缓存
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档