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