SpringBoot中Cache

简述

在SpringBoot中,Spring Cache对Cache进行了抽象,并且提供了@Cacheable,@CachePut,@CacheEvict等注解,SpringBoot应用基于Spring Cache,提供了基于内存实现的缓存管理器,也可以用于单体应用系统,也集成了Redis等缓存服务器,可以用于大型系统或者是分布式系统

Spring Boot可以很方便地使用缓存,极大的提高了应用系统的性能和吞吐量

关于Cache

在一些系统中我们需要通过Cache来缓存一些不经常改变的数据用以提高系统的性能和增加系统的吞吐量,避免直接访问数据库等低速的存储系统,同时也可以减轻数据库的压力,缓存的数据通常存放在访问速度更快的内存中或者是低延迟存取的存储器或者服务器上,应用系统的缓存通常有以下作用:

  • 缓存Web系统的输出,如伪静态页面
  • 缓存高频热点数据,减轻数据库压力

Cache的组件和概念

  • CacheManager,用来创建,管理,管理多个命名唯一的Cache,如组织机构缓存,菜单项的缓存,菜单树的缓存等
  • Cache,是类似Map那样的Key-Value存储结构,Value部分通常包含了缓存对象,通过Key来取得缓存对象
  • 缓存项,存放在缓存里的对象,常常需要实现序列化接口,以支持分布式缓存
  • Cache存储方式,缓存组件可以将对象放到内存或者其他的缓存服务器上,Spring Boot提供了一个机遇ConcurrentMap的缓存,同时也集成了Redis,JCache等缓存服务器
  • 缓存策略,通常Cache还可以有不同的缓存策略,如设置缓存的最大容量,缓存项的过期时间等
  • 分布式缓存,缓存通常按照缓存数据类型存放在不同缓存服务器上面,或者同一类型的缓存按照某种算法,不同Key的数据存放在不同的缓存服务器上
  • Cache Hit,从Cache中取得期望的缓存项,我们通常称之为缓存命中,如果没有命中则称之为Cache Miss(缓存丢失),意味着需要从数据来源处重新取出并放回Cache中
  • Cache Miss,缓存丢失,根据Key没有从缓存中找到对应的缓存项
  • Cache Evication,缓存清除操作
  • Hot Data,热点数据,缓存系统能够调整算法或者内部存储方式,使得最优可能频繁访问的数据能够被尽快访问到
  • On-Heap,Java分配对象都是在堆内存中,有最快的获取速度,由于JVM虚拟机的垃圾回收机制,缓存放入过多的对象会导致垃圾回收时间过长,从而有可能影响性能
  • Off-Heap,堆外内存,对象存放在虚拟机分配的堆外内存中,因此不受垃圾回收管理机制的管理,不影响系统性能,但堆外内存的对象要被使用,还要序列化成堆内对象,很多缓存工具会把不常用的对象放到堆外,把热点数据放到堆内

使用一/二级缓存服务器

使用Redis缓存,通过网络访问还是不如从内存中获取的性能好,所以通常称为二级缓存,从内存中取得缓存数据称为一级缓存,当应用系统需要查询缓存的时候,先从一级缓存里面进行查找,如果有则返回,如果在一级缓存中没有查询到数据,则再查询二级缓存

所以优先级:一级缓存(本地)>二级缓存(网络)

Spring Boot Cache

SpringBoot本身提供了一个基于ConcurrentHashMap的缓存机制,也集成了EhCache2.x,JCache,Redis等等

SpringBoot应用通常是通过注解的方式使用统一的缓存,只需要在方法上使用缓存注解即可,其缓存的具体实现依赖于选择的目标缓存管理器,使用@Cacheable

@Service
@EnableTransactionManagement
public class TestServiceImpl implements TestService {
    @Autowired
    SQLManager sqlManager ;

    @Override
    public List<Test> findTestAll() {
        List<Test> tests = new ArrayList<>();
        tests = sqlManager.all(Test.class);
        return tests;
    }

    @Override
    @Cacheable("TestId")
    public Test findTestById(Test test) {
        Integer id = test.getId();
        return sqlManager.unique(Test.class,id);
    }
}

例如这样,我们在这个服务类下面的findTestById()方法声明了@Cacheable注解,那么在实际调用这个方法前,会调用缓存管理器,取得名为"TestId"的缓存,此时,缓存项的Key就是方法参数id,如果缓存命中,则返回此值,如果没有找到,则进入实际的TestServiceImpl.findTestById(..)方法,在返回调用结果给调用者之前,其还会将此查询结果缓存以备下次继续使用

集成Spring Cache

在POM中添加如下依赖

<!--Cache-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

如果使用Spring自带的内存缓存管理器,需要在application.yml中进行配置

spring:
    cache:
        type: Simple

Simple只是用单体应用或者开发环境使用,或者是一个小微系统,通常你的应用是分布式应用,SpringBoot还支持集成更多的缓存服务器

  • Simple,基于ConcurrentHashMap实现缓存,使用单体应用开发或者开发环境使用
  • none,关闭缓存,比如开发阶段为了确保自己写的功能的正确性,可以先禁用缓存
  • redis,使用Redis作为缓存,还需要在pom中增加Redis依赖,在之前我们已经介绍过Redis了,如果想要了解请翻阅之前的文章
  • Generic,用户自定义缓存实现,用户需要实现org.springframework.cache.CacheManager
    .....

最后我们在启动类上需要添加注解@EnableCaching来打开缓存功能

注解

  • @Cacheable,作用在方法上,触发缓存读取操作(有缓存则不执行,没有缓存则执行,并保存到方法中)
  • @CacheEvict,作用在方法上,触发缓存失效操作(总是执行,用于删除缓存或者清空缓存)
  • @CachePut,作用在方法上,触发缓存更新操作
  • @Cache,作用在方法上,综合上面的各种操作,在有些场景下,调用业务会触发多种缓存操作
  • @CacheConfig,在类上设置当前缓存的一些公共设置,比如使用这个注解作用在上,就可以为此类的所有方法的缓存注解提供默认值,缓存的默认名称,缓存的KeyGenerator

注意,在缓存对象上面,最好事先序列化Serializable接口,以便切换到分布式缓存系统后不至于出错

Key

缓存的Key是非常重要的,Spring使用了KeyGenerator类来根据方法参数生成Key,有以下规则(注意,以下说的参数指的是构造方法的参数)

  • 如果只有一个参数,这个参数就是Key
  • 如果没有参数,则返回SimpleKey.EMPTY,比如构造系统的菜单树,因为系统只有唯一的一个菜单树,所以不用指定参数,Key就是SimpleKey.EMPTY
  • 如果有多个Key,则返回包含多个参数的SimpleKey

通常情况下,直接使用SpEL表达式,来指定Key

@Cacheable(cacheNames="test", key="#testId")
public Test findTest(Integer testId){...}

我们可以继续扩展一下,比如这样去使用它

@Cacheable(cacheNames="test", key="#user.Id")
public Test findTest(User user){...}

我们还可以通过使用条件表达式来指定某一条件下是否是需要缓存的,比如testId大于1000的都需要缓存,那么我们只需要在注解中的参数加上一个condition=#testId>1000即可

Redis Cache

在大多数情况下的分布式应用都会将缓存放在一台或者多台专门的缓存服务器上面

集成Redis缓存服务

了解Redis和 SpringBoot 调用Redis请看我之前的文章

配置

application.properties或者application.yml中进行对Redis和缓存的配置

基本的配置

spring:
    redis:
        host: 127.0.0.1
        password: 123456
        port: 6379
        jedis:
            pool:
                # 最大连接数
                max-active: -1
                # 连接池中的最小空闲连接
                max-idle: 10
  cache:
    type: redis

禁止缓存

spring:
    cache:
        type: redis

定制缓存

可以使用配置来定制缓存项的存活时间,或者是在缓存名中加上前缀

spring:
    cache:
        type: redis
        cache-names: user,test
        redis:
            time-to-live: 43200000
            use-key-prefix: true
            key-prefix: seale.cache

在上面我们就设置了redis缓存的存活时间为12个小时,并且使用key前缀为seale.cache,并且指定了缓存名为user,test的缓存生效


本作品采用知识共享署名 4.0 国际许可协议进行许可。

如果可以的话,请给我钱请给我点赞赏,小小心意即可!

Last modification:September 12, 2019
If you think my article is useful to you, please feel free to appreciate