设为首页 收藏本站
查看: 810|回复: 0

[经验分享] Spring Boot下的Redis缓存实战

[复制链接]

尚未签到

发表于 2018-11-3 06:11:36 | 显示全部楼层 |阅读模式
  最近在做的一个系统涉及到基础数据的频繁调用,大量的网络开销和数据读写给系统带来了极大的性能压力,我们决定引入缓存机制来缓解系统压力。

什么是缓存
  提起缓存机制,大概10个程序员总有5种不同的解释吧(姑且认为只有一半的程序员是通过复制粘贴来学习知识的),我也不能免俗的来说说我的理解。
  在回答这个问题之前,我们首先要搞清楚为什么要用缓存?
  历史唯物主义揭示了社会发展的基本动力是社会基础矛盾。
  运用到软件领域同样适用,一种新技术的出现必然是伴随着特定的矛盾产生的,而缓存的出现正是因为介质提供的实际处理响应速度和软件需求之间的矛盾,最终缓存机制的提出大大的缓解了这个矛盾,同时也印证了一句计算机领域的名言:
  Any problem in computer science can be solved by anther layer of indirection.
DSC0000.jpg

  缓存示意图
  结合上图我们可以看出缓存从某种意义上来说是一种代理,通过自身某一方面的优势弥补实际响应的局限性,理论上来说还是时间和空间的取舍权衡。
  下面列举几种常见的缓存
  1, 数据库缓存
  通过将查询语句缓存到内存中来减少文件系统的读写次数和程序响应时间
  2, 应用缓存
  将应用常用数据缓存到内存中来减少数据库访问,通过缓存减少了连接创建销毁的时间
  3, 用户端缓存
  通过一些用户端技术如浏览器和本地cookie等将用户常用数据进行缓存,减少网络连接的创建销毁,同时避免了网络传输的消耗

Spring中的缓存
  Spring从3.1版本开始就引入了基于注解的缓存支持,到现在已经发展的相当稳定了。Spring主要提供的是基于JSR107的抽象,对于缓存的具体实现可以是EhCache也可以是Redis。下面简单搬运一下几种注解的定义:
  @Cacheable  缓存的入口,首先检查缓存如果没有命中则执行方法并将方法结果缓存
  @CacheEvict  缓存回收,清空对应的缓存数据
  @CachePut   缓存更新,执行方法并将方法执行结果更新到缓存中
  @Caching    组合多个缓存操作
  @CacheConfig 类级别的公共配置
  原文链接:
  https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache

实际系统中的应用
  在了解了缓存的一些基础知识和框架的支持情况后,我们开始付诸实施,我们使用Redis作为缓存的具体实现。
  项目基于spring boot 2.0.0.RC1,maven的主要配置信息如下:
  
        org.springframework.boot
  
        spring-boot-starter-parent
  
        2.0.0.RC1
  

  

  
           
  
             org.springframework.boot
  
             spring-boot-starter-data-redis
  
             1.5.7.RELEASE
  
      
  

  首先明确缓存的位置,缓存的参与方可能在下面四层
  a)      客户端
  b)      接口层
  c)      服务层
  d)      数据层
  在选择位置的时候出现的分歧是离客户端更近一些还是离缓存所有方更近,具体到我们系统中就是缓存放在a还是b,各有优劣。
  放在客户端可以降低网络消耗,放在服务端可以明确管理职责,最终我们选择了放在b牺牲一部分的性能消耗来保证数据的完整性和一致性。
  下面通过两个场景来说明缓存的维护
  1,   缓存创建(接口层@Cacheable)
DSC0001.jpg

  2,   缓存更新(服务层@CacheEvict, @Caching)
DSC0002.jpg

  注:考虑配置数据的修改频率较低,并且配置数据的缓存结构比较复杂,每次数据修改和新增会删除相应的缓存,再由接口层调用来重新加载缓存
  接下来就是实现了,
  首先需要开启缓存功能,在主程序上加上@EnableCaching注解即可
  然后是相关注解的代码:
   @Cacheable(value="icare_region",key="('c_').concat(#companyId)")  
   public List loadRegionByCompIdRest(@RequestParam("companyId") Integer companyId){
  
          List regions = regionService.selectRegionsByCompId(companyId);
  
          return regions;
  
      }
  

  

  
   @CacheEvict(cacheNames="icare_region", key="('c_').concat(#region.companyId)")
  
   public void saveRegion(Region region) {
  
        regionMapper.insert(region);
  
   }
  

  

  
   @Caching(evict = { @CacheEvict(cacheNames="icare_region", key="('r_').concat(#region.regionId)"), @CacheEvict(cacheNames="icare_region", key="('c_').concat(#region.companyId)") })
  
   public void updateRegion(Region region) {
  
        Region existRegion = regionMapper.selectByPrimaryKey(region.getRegionId());
  
        region.setStatus(existRegion.getStatus());
  
        region.setCreateTime(existRegion.getCreateTime());
  
        region.setUpdateTime(new Date());
  
        regionMapper.updateByPrimaryKey(region);
  
   }
  最后就是测试了
  在如何确定程序按照我们的意图走到了缓存而非原来的数据库调用的时候,我们使用了druid的sql监控功能,如图直接观察sql的执行次数就可以:
DSC0003.jpg


问题和扩展
  先说个碰到的具体问题,我们在使用Redis的时候选择从网上拷贝了一个RedisConfig的文件来扩展KeyGenerator,RedisTemplate和CacheManager。但是当我们再引入了spring boot的dev-tool的时候,上面的缓存实现会报错提示ClassCast Exception。
  最终在官网找到答案:在老版本的CacheManager中没有考虑序列化和反序列化的ClassLoader问题,导致序列化和反序列化的ClassLoader不一致;最新的修复就是指定了CacheManager使用的ClassLoader。而网上现在流传的都是老版本的CacheManager,反而把最新版本的修复覆盖掉了…
  问题链接:https://github.com/spring-projects/spring-boot/issues/11822
  此外,我们现在实现的这种缓存还有诸多限制,也是我们要扩展的方向
  1, 无法设置失效时间
  Redis是支持设置失效时间的,但是spring 抽象中没有提供相关支持。
  2, 无法统计命中率等指标
  无法统计命中率就没有办法判定缓存的失效和替换,当然这些都是在缓存变大的情况下需要考虑的



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-629953-1-1.html 上篇帖子: MISCONF Redis is configured to save RDB snapshots-lang8027 下篇帖子: Redis 4 部署实录
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表