- 概述
- 配置
- 常用注解
- @Cacheable
- 含义:
- 默认行为:
- 自定义:
- SpEL:
- @CacheEvict
- @CachePut(略)
- 不足:
官网:https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache
描述:
-
从版本 3.1 开始,Spring 框架支持向 Spring 应用程序添加缓存。与事务支持类似,缓存抽象允许一致地使用各种缓存解决方案,同时对代码的影响最小。
-
在Spring Framework 4.1中,缓存抽象得到了显着扩展,支持JSR-107注释和更多自定义选项。
引入依赖
- cache:
org.springframework.boot spring-boot-starter-cache
- 引入redis
org.springframework.boot spring-boot-starter-data-redis
配yaml,type是redis
spring:
cache:
type: redis
redis:
# 单位是毫秒
time-to-live: 3600000
# 缓存null值,可防止缓存穿透
cache-null-values: true
#key-prefix: CACHE_ # 设置缓存前缀,如果不设置,则用缓存名字作为前缀
# 是否使用前缀,如果不用前缀,键只有key值
use-key-prefix: true
主启动类标@EnableCaching 开启缓存
常用注解 @Cacheable 含义:- 当前方法需要缓,如果缓存中有,方法不调用
- 如果缓存中没有,会调用方法,并把结果放入缓存中
- 如果缓存中有,方法不调用
- key默认自动生成:缓存名字(必须指定)::SimpleKey
- 默认使用jdk序列化机制,将数据存到redis
- 默认ttl-1
- 指定生成的缓存使用的key:使用属性key指定,key接收一个SpEL表达式(下文有SpEL),如果要自定义字符串,需要写在单引号里面,例如:key = "'myKey'"
- 指定缓存存活时间:在配置文件中配置:spring.cache.redis.time-to-live(以毫秒为单位)
- 将数据保存为json格式:org.springframework.data.redis.cache.RedisCacheConfiguration
import org.springframework.boot.autoconfigure.cache.CacheProperties; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; @Configuration @EnableCaching public class MyCacheConfig { @Bean RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { CacheProperties.Redis redisProperties = cacheProperties.getRedis(); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); //用json序列化,注意serializeValuesWith并没有设置值,只是返回一个新的对象 config = config.serializeValuesWith( RedisSerializationContext.SerializationPair.fromSerializer( new GenericJackson2JsonRedisSerializer())); if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } if (redisProperties.getKeyPrefix() != null) { config = config.prefixCacheNameWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; } }
SpEL:配置原理见源码:
- 摘自官网:https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-spel-context
| Name | Location | Description | Example |
|---|---|---|---|
methodName | Root object | The name of the method being invoked | #root.methodName |
method | Root object | The method being invoked | #root.method.name |
target | Root object | The target object being invoked | #root.target |
targetClass | Root object | The class of the target being invoked | #root.targetClass |
args | Root object | The arguments (as array) used for invoking the target | #root.args[0] |
caches | Root object | Collection of caches against which the current method is run | #root.caches[0].name |
Argument name | Evaluation context | Name of any of the method arguments. If the names are not available (perhaps due to having no debug information), the argument names are also available under the where stands for the argument index (starting from ).#a<#arg>#arg0 | #iban or (you can also use or notation as an alias).#a0#p0#p<#arg> |
result | Evaluation context | The result of the method call (the value to be cached). Only available in expressions, expressions (to compute the ), or expressions (when is ). For supported wrappers (such as ), refers to the actual object, not the wrapper.unlesscache putkeycache evictbeforeInvocationfalseOptional#result | #result |
失效模式,当更新数据库的时候把对应缓存也删了。
一次性删多个缓存?
- 使用@Caching,指定多个值
- 使用@CacheEvict(value = "category",allEntries = true)(推荐)
把所有在category分区的数据都删了
双写模式,写入数据库的同时,写入缓存中
不足:读模式可能出现的问题及解决:
-
缓存穿透(查询null值,总是查数据库):Spring Cache已解决!配置缓存空值即可
-
缓存击穿(大并发查询正好过期的数据):设置注解属性sync=true
-
缓存雪崩(大量key同时过期):加随机时间。设置过期时间,不是同时缓存的,不会同时过期!
写模式可能出现的问题及解决:
- 读多写少:加读写锁
- 引入canal,感知mysql更新去更新数据库
- 读多写多:直接查数据库
总结:
- 常规数据(读多写少,即时性、一致性要求不高的数据),完全可以用spring cache;写模式,只要缓存的数据有过期时间就足够了
- 特殊数据:特殊设计



