SpringBoot + Redis缓存的基本使用

前言

在数据量少或者调用量少的时候,性能问题几乎不存在,但是当数据量上升之后,接口压力也随之上升,会出现慢查询等情况,这时候缓存就起到了重大的作用。

基本使用

引入依赖

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

配置

spring:
   redis:
    ## Redis数据库索引(默认为0)
    database: 0
    ## Redis服务器地址
    host: 127.0.0.1
    ## Redis服务器连接端口
    port: 6379
    ## Redis服务器连接密码(默认为空)
    password: root
    jedis:
      pool:
        ## 连接池最大连接数(使用负值表示没有限制)
        #spring.redis.pool.max-active=8
        max-active: 8
        ## 连接池最大阻塞等待时间(使用负值表示没有限制)
        #spring.redis.pool.max-wait=-1
        max-wait: -1
        ## 连接池中的最大空闲连接
        #spring.redis.pool.max-idle=8
        max-idle: 8
        ## 连接池中的最小空闲连接
        #spring.redis.pool.min-idle=0
        min-idle: 0
    ## 连接超时时间(毫秒)
    timeout: 1200

开启缓存

配置类 或者 启动类 上添加 @EnableCaching 启动缓存

然后在需要缓存的地方加如下注解便可(一般用在service层):

注解 作用
@Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@CacheEvict 清空缓存
@CachePut 保证方法被调用,又希望结果被缓存

@Cacheable

可以将运行结果缓存,以后查询相同数据,直接从缓存去,不需要调用方法

常用属性:

  • cacheNames/Value:指定缓存组件的名字
  • key:缓存数据时使用的 key,默认是使用方法参数的值 (可以使用 spEL 表达式编写)
  • keyGenerator:key 生成器,key 和 keyGenerator 是二选一的
  • cacheManager:用来指定缓存管理器,从那个缓存管理器里面获取缓存
  • condition:符合指定条件下才进行缓存
  • unless:否定缓存,当 unless 为 true 时,方法返回值不缓存,也可以获取到结果进行判断 (通过 #result 获取方法结果)
  • sybc:是否使用异步

cacheNames/Value

指定缓存组件名称,将方法的返回结果放在那个缓存中,可以说数组的方式,支持定义多个缓存

@Cacheable(cacheNames = "emps") // 或者 @Cacheable(cacheNames = {"emps","heping"})
public Employee getEmployee(Integer id) {
    System.out.println("查询" + id);
    Employee employee = employeeMapper.getEmpById(id);
    return employee;
}

key

缓存时使用的key,默认是方法参数的值,可以使用 spEL 表达式,这个值会和 cacheNames 组合起来形成一个 redis key

@Cacheable(cacheNames = "emps", key = "#root.methodName + '[' + #id + ']'")
public Employee getEmployee(Integer id) {
    System.out.println("查询" + id);
    Employee employee = employeeMapper.getEmpById(id);
    return employee;
}

keyGenerator

key的生成器,用来自己指定 key 的生成器

//在Reids配置类中添加如下代码 创建一个自定义的key生成器
@Bean("myKeyGenerator")
public KeyGenerator cacheKeyGenerator() {
    return (target, method, params) -> {
        StringBuilder sb = new StringBuilder();
        for (Object obj : params) {
            // 由于参数可能不同, hashCode肯定不一样, 缓存的key也需要不一样
            sb.append(JSON.toJSONString(obj).hashCode());
        }
        return sb.toString();
    };
}
@Cacheable(cacheNames = "emps", keyGenerator = "myKeyGenerator")//这里的 keyGenerator 写我们上面自定义 Bean 的名称
public Employee getEmployee(Integer id) {
    System.out.println("查询" + id);
    Employee employee = employeeMapper.getEmpById(id);
    return employee;
}

这样缓存 key 的生成规则就安装自定义的 keyGenerator 来生成

注意:@Cacheable 的属性,key 和 keyGenerator 使用的时候,一般二选一

condition

符合条件下才进行缓存,方法返回的数据要不要缓存,可以做一个判断

@Cacheable(cacheNames = "emps", condition = "#id > 1") //表示 id 值大于 1 才进行缓存
public Employee getEmployee(Integer id) {
    System.out.println("查询" + id);
    Employee employee = employeeMapper.getEmpById(id);
    return employee;
}

unless

否定缓存,当 unless 为 true 时,不进行缓存

@Cacheable(cacheNames = "emps", unless = "#id > 1") //表示 id 值大于 1 不会进行缓存
public Employee getEmployee(Integer id) {
    System.out.println("查询" + id);
    Employee employee = employeeMapper.getEmpById(id);
    return employee;
}

sync

是否使用异步,默认是方法执行完,以同步的形式进行缓存

@CacheEvict

清除缓存,其中大部分属性和 @Cacheable 基本一样,不做过多赘述

allEntries

是否清除缓存中的所有元素,默认为 false,表示不需要,当为 true 时,将忽略指定的 key,清除全部缓存

@CacheEvict(value = "users", allEntries = true)
public void delete(Integer id) {
    System.out.println("delete user by id: " + id);
}

beforeInvocation

清除操作模式是在目标方法成功执行后才触发的,如果方法执行异常,则不触发,使用 beforeInvocation 可以改变触发的时间,当为 ture 时,会在调用目标方法前清除缓存

@CacheEvict(value = "users", beforeInvocation = true)
public void delete(Integer id) {
    System.out.println("delete user by id: " + id);
}

@CachePut

更新缓存,里面的属性基本和 @Cacheable 一样,这里忽略

@CachePut("users")//每次都会执行方法,并将结果存入指定的缓存中
public User find(Integer id) {
    return null;
}

@CacheConfig

上面三个注解中,有些属性是重复的,就可以同一写在 @CacheConfig 中

//下面的@Cacheable,@CacheEvict,@CachePut 的 cacheNames 属性如果不重新指定的话,默认都在 user 下
@CacheConfig(cacheNames = {"user"})
public class UserServiceImpl implements IUserService {
    
}

@Caching

在一个方法中,可能存在多个缓存注解,我们可以用 @Caching 将它们写在一起

@Caching(cacheable = @Cacheable(value = "users"), 
         evict = { 
             @CacheEvict(value = "cache2"),
             @CacheEvict(value = "cache3", allEntries = true) 
         }
)
public User find(Integer id) {
    return null;
}

自定义缓存注解

上面的 @Caching 组合,会让代码显得比较乱,此时可以把它们写在自定义注解中

@Caching(cacheable = @Cacheable(value = "users"), 
         evict = { 
             @CacheEvict(value = "cache2"),
             @CacheEvict(value = "cache3", allEntries = true) 
         }
)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserFineCache {
}
@UserFineCache
public User find(Integer id) {
    return null;
}

Redis缓存的更多配置

//Redis缓存配置
@Bean
public RedisCacheConfiguration configuration(){
    return RedisCacheConfiguration.defaultCacheConfig()
        //设置过期时间 1天
        .entryTtl(Duration.ofDays(1))	
        //设置编码
        .serializeValuesWith(
            RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())
    	);
}
//Redis管理
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory,RedisCacheConfiguration configuration) {
    return RedisCacheManager
        .builder(factory)
        //redis默认缓存配置,configuration 对应上文的配置
        .cacheDefaults(configuration)
        //post 对应上文缓存注解中的 cacheNames 的值,不同的 cacheNames可以单独配置 redis缓存配置,这里单独配置了过期时间60分粥
        .withCacheConfiguration("post",configuration.entryTtl(Duration.ofMinutes(60)))
        .build();
}

参考

文章1:https://xie.infoq.cn/article/001e0f5ab65fa7dd1484c51e5

文章2:https://blog.csdn.net/dreamhai/article/details/80642010

文章3:https://blog.csdn.net/qq_37989070/article/details/113932711

文章4:https://segmentfault.com/a/1190000022548904