Spring Boot下的Redis缓存实战

最近在做的一个系统涉及到基础数据的频繁调用,大量的网络开销和数据读写给系统带来了极大的性能压力,我们决定引入缓存机制来缓解系统压力。

什么是缓存

提起缓存机制,大概10个程序员总有5种不同的解释吧(姑且认为只有一半的程序员是通过复制粘贴来学习知识的),我也不能免俗的来说说我的理解。

在回答这个问题之前,我们首先要搞清楚为什么要用缓存?

历史唯物主义揭示了社会发展的基本动力是社会基础矛盾。

运用到软件领域同样适用,一种新技术的出现必然是伴随着特定的矛盾产生的,而缓存的出现正是因为介质提供的实际处理响应速度和软件需求之间的矛盾,最终缓存机制的提出大大的缓解了这个矛盾,同时也印证了一句计算机领域的名言:

Any problem in computer science can be solved by anther layer of indirection.

缓存示意图

结合上图我们可以看出缓存从某种意义上来说是一种代理,通过自身某一方面的优势弥补实际响应的局限性,理论上来说还是时间和空间的取舍权衡。

下面列举几种常见的缓存

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 <version>2.0.0.RC1</version>,maven的主要配置信息如下:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RC1</version>
</parent>
<dependencies>
           <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-redis</artifactId>
             <version>1.5.7.RELEASE</version>
      </dependency>
</dependencies>

首先明确缓存的位置,缓存的参与方可能在下面四层

a)      客户端

b)      接口层

c)      服务层

d)      数据层

在选择位置的时候出现的分歧是离客户端更近一些还是离缓存所有方更近,具体到我们系统中就是缓存放在a还是b,各有优劣。

放在客户端可以降低网络消耗,放在服务端可以明确管理职责,最终我们选择了放在b牺牲一部分的性能消耗来保证数据的完整性和一致性。

下面通过两个场景来说明缓存的维护

1,   缓存创建(接口层@Cacheable)

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

注:考虑配置数据的修改频率较低,并且配置数据的缓存结构比较复杂,每次数据修改和新增会删除相应的缓存,再由接口层调用来重新加载缓存

接下来就是实现了,

首先需要开启缓存功能,在主程序上加上@EnableCaching注解即可

然后是相关注解的代码:

   @Cacheable(value="icare_region",key="('c_').concat(#companyId)")
   public List<Region> loadRegionByCompIdRest(@RequestParam("companyId") Integer companyId){
          List<Region> 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的执行次数就可以:

问题和扩展

先说个碰到的具体问题,我们在使用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, 无法统计命中率等指标

无法统计命中率就没有办法判定缓存的失效和替换,当然这些都是在缓存变大的情况下需要考虑的

原文地址:http://blog.51cto.com/luischen/2083531

时间: 2024-10-12 12:59:42

Spring Boot下的Redis缓存实战的相关文章

spring boot项目之redis缓存

以程序为例,tomcat里是我们的java应用,第一步会先从redis获取,如果没有,就会从db上面获取,如果取出了,他还会把取出的东西重新写回redis 使用缓存的步骤: 一.在SellApplication上添加注解@EnableCaching 如果你想引入缓存的话,可以在pom上直接写入以下代码 二.在BuyerProductController.list()方法上添加注解@Cacheable(cacheNames = "product", key = "123&quo

Spring Boot 2.x Redis多数据源配置(jedis,lettuce)

Spring Boot 2.x Redis多数据源配置(jedis,lettuce) 96 不敢预言的预言家 0.1 2018.11.13 14:22* 字数 65 阅读 727评论 0喜欢 2 多数据源最终表现其实就是 redis connection factory 不同 springboot 默认的redis配置维护了一套 connection factory 自己维护一套 connection factory 即可实现 application.yml spring: redis: # 默

Windows环境下使用Redis缓存工具的图文详细方法

网上找了两篇关于Redis的博客,记录下! Java 使用Redis缓存工具的图文详细方法 Windows环境下使用Redis缓存工具的图文详细方法

spring boot 中使用redis session

spring boot 默认的httpsession是存在内存中.这种默认方式有几个缺点:1.当分布式部署时,存在session不一致的问题:2.当服务重启时session就会丢失,这时候用户就需要重新登陆,可能导致用户数据丢失.通常会使用redis来保存session. 在spring boot中利用redis来保存session是非常简单.只需要简单的几步就可以了.可以参考官方教程.https://docs.spring.io/spring-session/docs/current/refe

Spring Boot 中集成 Redis 作为数据缓存

只添加注解:@Cacheable,不配置key时,redis 中默认存的 key 是:users::SimpleKey [](1.redis-cli 中,通过命令:keys * 查看:2.key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = "#p0"):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档). 相关文章 网址 SpringBo

spring boot学习(十三)SpringBoot缓存(EhCache 2.x 篇)

SpringBoot 缓存(EhCache 2.x 篇) SpringBoot 缓存 在 Spring Boot中,通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManager),Spring Boot根据下面的顺序去侦测缓存提供者: * Generic * JCache (JSR-107) * EhCache 2.x * Hazelcast * Infinispan * Redis * Guava * Simple 关于 Spring Boot 的缓存机制: 高速缓

Spring Boot 整合 Lettuce Redis

SpringBoot?是为了简化?Spring?应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以轻易的搭建出一个 WEB 工程 Spring Boot?除了支持常见的ORM框架外,更是对常用的中间件提供了非常好封装,随着Spring Boot2.x的到来,支持的组件越来越丰富,也越来越成熟,其中对Redis的支持不仅仅是丰富了它的API,更是替换掉底层Jedis的依赖,取而代之换成了Le

Spring Boot:使用Redis存储技术

综合概述 Redis是一个开源免费的高性能key-value数据库,读取速度达110000次/s,写入速度达81000次/s.Redis支持丰富的数据类型,如Lists, Hashes, Sets 及 Ordered Sets 数据类型.Redis的所有操作都是原子性的,要么成功执行要么失败完全不执行.另外还可以通过MULTI和EXEC指令包起来支持事务.此外,Redis还具备丰富的特性 ,比如支持发布/订阅(publish/subscribe)模式,可以充当简单的消息中间件,还支持通知, ke

spring boot + mybatis + druid + redis

接上篇,使用redis做缓存 新建spring boot 工程,添加pom引用 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring