Springboot整合缓存

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: Springboot整合缓存

一、为什么使用缓存

一个应用主要瓶颈在于数据库的IO,大家都知道内存的速度是远远快于硬盘的速度(即使固态硬盘与内存也无法比拟)。

应用之中经常会遇到某些数据变化的可能性很小。假如我们使用传统的方式每次都通过接口与数据库打交道去请求获得,每次既消耗了内存资源、网络资源、数据库资源、CPU资源,又导致大量的时间耗费在数据库查询,及远程方法调用上,从而导致程序性能的恶化。

这种场景就是需要使用缓存来解决这类问题。我们把数据缓存在内存之中,以后每次获取直接内存之中获得;使得程序获得极大的性能提升。

1、SpringCache介绍

SpringCache相当于一个抽象接口,在其底层可以切换各种Cache的实现,当缓存数据时,只需要注入SpringCache对应的注解,即可实现缓存的功能,大大的提高了缓存的效率,这就是Spring自带缓存SpringCache的使用原理

Springcache默认的缓存实现是simple(内存级),当然可以更换成其他实现,常见的有如下

EhCache:此缓存框架一直伴随着Spring,Hibernate,Mybatis等等。在SpringBoot出来之前都已经广泛的使用来做为一级缓存

Guava: Google出品的框架,也支持缓存

Redis:在我们日常开发之中经常使用的;并且被大众广泛接受的速度极快的缓存

memcached:是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。

Spring缓存的接口:

org.springframework.cache.Cache ;

org.springframework.cache.CacheManager

这两个接口都在context中,一个是用来提供缓存,一个是用来提供管理缓存。

CacheManager是Spring提供的各种缓存技术抽象接口,

Cache接口包含缓存的各种操作(增加、删除、获得缓存,我们一般不会直接和此接口打交道)。

Spring支持的CacheManager实现如下图:

使用缓存需要引入的依赖是

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

2、注解

2.1、配置注解

EnableCaching

标注于SpringBoot应用启动类上,添加此注解表示开启Spring Cache缓存;移除表示关闭缓存。如果在全局配置文件中添加如下配置,即使在启动类上标注EnableCaching注解,Spring Cache缓存然后是关闭状态。

spring:
  cache:
    type: none
 

如果应用中自定义独立于Spring容器的缓存,则不受此配置影响。

CacheConfig

标注于类上,更具体的说是标注于业务服务类上。统一配置如下参数信息:

参数 含义 使用说明
cacheManager 缓存管理器 缺省指首要的CacheManager
cacheNames 缓存名
keyGenerator key值生成器

在类上统一进行配置,类下的方法自动继承相应的配置。

2.2、缓存注解

Cacheable

添加缓存的核心注解,分两种情况:一是对应key值未有缓存数据,先执行方法,然后根据condition和unless条件决定是否添加缓存;二是对应key值已有缓存,不执行方法体,直接返回数据。

    /**
     *value:自定义缓存空间名
     *key:缓存的key
     */
    @Cacheable(value = "cacheSpace",key ="#id" )
    public List list(String id){
        return Arrays.asList("1","2","3");
    }

基础参数

参数 含义 使用说明
cacheManager 缓存管理器 缺省指首要的CacheManager
cacheNames 缓存名
keyGenerator key值生成器
key key值

参数keyGeneratorkey是互斥的,当key存在时keyGenerator配置自动失效。

高级参数

参数 含义 默认值 使用说明
condition 缓存条件 指示满足条件方执行缓存操作,一般使用参数作为条件
unless 否定缓存 当条件为 true ,方法的返回值不会被缓存
sync 同步状态 false 表示将方法执行结果以何种方式存入缓存

CachePut

更新缓存注解。不管对应key值是否有缓存数据,都执行。

基础参数

参数 含义 使用说明
cacheManager 缓存管理器 缺省指首要的CacheManager
cacheNames 缓存名
keyGenerator key值生成器
key key值

高级参数

参数 含义 使用说明
condition 缓存条件 指示满足条件方执行缓存操作,一般使用参数作为条件
unless 否定缓存 当条件为 true ,方法的返回值不会被缓存

CacheEvict

主动清除缓存注解。

基础参数

参数 含义 使用说明
cacheManager 缓存管理器 缺省指首要的CacheManager
cacheNames 缓存名
keyGenerator key值生成器
key key值

高级参数

参数 含义 默认值 使用说明
condition 缓存条件 指示满足条件方执行缓存操作,一般使用参数作为条件
allEntries 所有缓存 false 表示是否清空当前CacheName对应的所有缓存
beforeInvocation 调用前 false 表示是否在方法调用前清空缓存

3、实现方式

默认cache接口的实现simple(内存级),直接使用上面的注解即可,没有什么需要配置的,可以自己选择合适的缓存,

3.1、Ehcache作为缓存

可以在spring-boot-dependencies发现有Echcache依赖,所以我们只需引入依赖,不需要版本

需要导入的依赖

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

配置

spring:
  cache:
    type: EHCACHE

还需要一个Ehcache的配置文件ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--
       diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
       user.home – 用户主目录
       user.dir  – 用户当前工作目录
       java.io.tmpdir – 默认临时文件路径
     -->
    <diskStore path="java.io.tmpdir/Tmp_EhCache"/>
    <!--
       defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
    <!--
      name:缓存名称。
      maxElementsInMemory:缓存最大数目
      maxElementsOnDisk:硬盘最大缓存个数。
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。
      overflowToDisk:是否保存到磁盘,当系统当机时
      timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
      timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
      diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
      diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
      clearOnFlush:内存数量最大时是否清除。
      memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
      FIFO,first in first out,这个是大家最熟的,先进先出。
      LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
      LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->
    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>
 
    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
 
</ehcache>

使用方式还是用上面的注解

3.2、redis作为缓存

引入依赖

<!--springcache依赖-->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-cache</artifactId>
   </dependency>
   
<!--使用redis作为缓存-->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
   </dependency>

注意:这里是把redis作为cache实现进行整合,即springcache整合redis,并不是springboot整合redis

配置

spring:
  cache:
    type: EHCACHE
    redis:
      #是否缓存空值
      cache-null-values: true
      #是否使用前缀
      use-key-prefix: true
      #前缀
      key-prefix: test
      #缓存时间
      time-to-live: 10s
  redis:
    host: localhost
    port: 6379

使用方式还是用上面的注解

3.3、memcached作为缓存

下载安装memcached

地址Windows 下安装 Memcached | 菜鸟教程

下载后解压,cmd执行exe文件

memcached.exe -d install

安装如果出现failed to install servise or service is already installed,此时使用管理员身份打开cmd执行上面安装命令即可

然后启动和停止服务

memcached.exe -d start

memcached.exe -d stop

boot对memcached没有提供相应的整合,需要使用编码方式实现客户端管理,memcached客户端有一下三个

memcached client for java:最早期客户端 稳定可靠 用户群广(类似redis中的jedis)

SpyMemcached :效率更高

Xmemcached:并发处理更好

这里演示三个最好的Xmemcached使用

<dependency>
  <groupId>com.googlecode.xmemcached</groupId>
  <artifactId>xmemcached</artifactId>
  <version>2.4.7</version>
</dependency>

配置bean

@Configuration
public class XMemcachedConfig {
    @Bean
    public MemcachedClient memcachedClient() throws IOException {
        XMemcachedClientBuilder xMemcachedClientBuilder = new XMemcachedClientBuilder("localhost:11211");
        //自己根据需要设置其他参数
        xMemcachedClientBuilder.setConnectionPoolSize(20);
        MemcachedClient memcachedClient = xMemcachedClientBuilder.build();
        return memcachedClient;
    }
}

测试

    @RequestMapping("mSet")
    @ResponseBody
    public String mSet() throws InterruptedException, TimeoutException, MemcachedException {
        memcachedClient.set("token",0,Arrays.asList("1","2","3"));
        return "ok";
    }
    @RequestMapping("mGet")
    @ResponseBody
    public String mGet() throws InterruptedException, TimeoutException, MemcachedException {
        return memcachedClient.get("token").toString();
    }

详情教程

Memcached 教程 | 菜鸟教程

3.4、jetcache作为缓存

上面几种缓存技术有以下几点特点

1、配置方式多样性,有xml中配置的、有yml配置文件的、有代码中写的,比较散乱

2、缓存有本地方案和远程方案(redis)

阿里提供的jetcache设定了本地缓存和远程缓存的多级缓存解决方案,用来替代spring-cache的

  • 本地缓存

       LinkedHashMap

       Caffeine

  • 远程缓存

       Redis(本文选择远程方案)

       Tair

使用jetcache之后就需要引入spring-boot-starter-cache,加入jetcache依赖

<dependency>
      <groupId>com.alicp.jetcache</groupId>
      <artifactId>jetcache-starter-redis</artifactId>
      <version>2.6.2</version>
    </dependency>

注意:boot整合jetcache只需引入上面依赖即可,如果不是boot整合,需要引入的是

jetcache anno和jetcache core两个依赖

jetcache远程&本地缓存方案

配置

jetcache:
  #area是否进入拼接到缓存的key
  areaInCacheName: false
  #远程配置 默认default即可,远程方案使用redis ,其中poolConfig必须配置
  remote:
    default:
      type: redis
      host: localhost
      port: 6379
      keyConvertor: fastjson
      valueEncode: java
      valueDecode: java
      poolConfig:
        maxTotal: 50
        minIdle: 5
        maxIdle: 20
    test:
      type: redis
      host: localhost
      port: 6379
      keyConvertor: fastjson
      valueEncode: java
      valueDecode: java
      poolConfig:
        maxTotal: 50
        minIdle: 5
        maxIdle: 20
    #本地缓存配置
    local:
      default:
        type: linkedhashmap
        keyConvertor: fastjson
        valueEncode: java
        valueDecode: java

启动类开启缓存

@SpringBootApplication
//jetache缓存开启
@EnableCreateCacheAnnotation
public class TestApplication {
 
  public static void main(String[] args) {
    SpringApplication.run(TestApplication.class, args);
  }
}
    /**
     *定义缓存对象
     * area:和配置文件配置的要对应,如果配置文件remote只有default 这个area可以省略
     * name:空间配置 最好使用下划线或者其他符号和缓存的key隔开
     * expire:过期时间 默认单位秒
     * timuit:可选配置,默认单位秒
     * cacheType:当前缓存类型,只在本地缓存还是远程,
     *            取值CacheType.BOTH,CacheType.REMOTE CacheType.LOCAL三个取值 默认REMOTE
     */
    @CreateCache(area = "test",name = "testCache_",expire = 3600,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.REMOTE)
    private Cache<String,String> jetCache;
 
    public void jetCachePut(){
        //redis中的key是:test_testCache_token
        jetCache.put("token","111111111111111111");
    }
    public String jetCacheGet(){
        return jetCache.get("token");
    }

jetcache方法缓存

需要在启动类开启方法缓存注解

@SpringBootApplication
//jetache缓存开启
@EnableCreateCacheAnnotation
//开启方法缓存
@EnableMethodCache(basePackages = {"com.test"})
public class TestApplication {
 
  public static void main(String[] args) {
    SpringApplication.run(TestApplication.class, args);
  }
}

使用如下

    /**
     * 方法缓存注解(使用方法缓存时放入缓存的实体类要实现序列化接口)
     */
    @Cached(name = "user_",key="#id",expire = 3600,cacheType = CacheType.BOTH)
    public User getById(Integer id){
       return userDao.getById(id);
   }
 
    /**
     * 更新数据时同时把缓存中的数据更新
     * vaule表示新的数据
     */
   @CacheUpdate(name = "user_",key="#user.id",value = "#user")
   public void  updateUser(User user){
        userDao.update(user);
   }
    /**
     * 删除数据时候同时删除缓存中的数据
     * vaule表示新的数据
     */
   @CacheInvalidate(name = "user_",key = "#id")
    public void  delUser(Integer id){
       userDao.delUser(id);
   }

如果同一套系统部署到两台机器,a机器改了数据但是b没有收到通知,此时可以使用cache提供的一个定义刷新缓存的注解

    @Cached(name = "user_",key="#id",expire = 3600,cacheType = CacheType.BOTH)
    @CacheRefresh(refresh = 5)
    public User getById(Integer id){
       return userDao.getById(id);
   }

查看jetcache缓存报告报表

只需增加一个配置

jetcache:
  #查看缓存报告:1min后
  statIntervalMinutes: 1

控制台定时打印缓存命中相关

3.5、j2cache

j2cache是一个缓存整合框架,使各种缓存配合使用,自身不提供缓存

使用参考视频

实用开发篇-116-j2cache相关配置_哔哩哔哩_bilibili


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
缓存 NoSQL Java
SpringBoot实现缓存预热的几种常用方案
SpringBoot实现缓存预热的几种常用方案
|
3月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
5月前
|
缓存 NoSQL Java
在 Spring Boot 应用中使用 Spring Cache 和 Redis 实现数据查询的缓存功能
在 Spring Boot 应用中使用 Spring Cache 和 Redis 实现数据查询的缓存功能
277 0
|
3月前
|
缓存 NoSQL Java
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
|
3月前
|
缓存 Java Spring
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
|
5月前
|
缓存 Java 数据库
springboot数据库及缓存常用依赖及配置
springboot数据库及缓存常用依赖及配置
|
5月前
|
缓存 NoSQL Java
案例 采用Springboot默认的缓存方案Simple在三层架构中完成一个手机验证码生成校验的程序
案例 采用Springboot默认的缓存方案Simple在三层架构中完成一个手机验证码生成校验的程序
108 5
|
5月前
|
存储 缓存 NoSQL
SpringBoot配置第三方专业缓存技术Redis
SpringBoot配置第三方专业缓存技术Redis
46 4
|
5月前
|
缓存 监控 NoSQL
SpringBoot配置第三方专业缓存技术jetcache方法缓存方案
SpringBoot配置第三方专业缓存技术jetcache方法缓存方案
352 1
|
5月前
|
缓存 Java 微服务
Springboot微服务整合缓存的时候报循环依赖的错误 两种解决方案
Springboot微服务整合缓存的时候报循环依赖的错误 两种解决方案
74 1