Spring Boot 缓存 与 Redis
缓存是提升接口响应速度和降低数据库压力的重要手段。
Redis | 通过Socket访问到缓存服务,效率比EnCache低,对集群和分布式支持友好。 |
EnCache | 纯Java的进程缓存,直接在JVM中进行缓存,速度快、效率高,但对分布式集群的支持不太好。 |
ConcurrentHashMap Manager | Spring Boot 默认提供的缓存管理器,线程安全,官方明确不建议在生产环境使用。 |
表 Java缓存方案对比
1 缓存注解
Spring Boot 提供了缓存抽象层,并结合JSR-107标准,提供了多个缓存注解。
@Cacheable | 查询缓存。 |
@CachePut | 更新缓存。 |
@CacheEvict | 删除缓存。 |
表 常用的缓存注解
这些注解作用于方法级别,在不侵入业务逻辑的前提下,实现缓存管理。它们是基于AOP实现的,有以下注意项:
- 只能用于public方法上。
- 只有当调用方通过Spring代理调用该方法时才生效。(同一个类中直接调用不生效)
- 需要启用缓存功能。@EnableCaching。
1.1 缓存key 生成策略
缓存注解keyGenerator字段用于指定该方法缓存key的生成策略。
图 key 生成策略需实现的接口
@Beanpublic KeyGenerator paramsKeyGenerator() {return ((target, method, params) -> {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getSimpleName());sb.append("::");sb.append(method.getName());for (Object obj : params) {sb.append("::");sb.append(obj.toString());}return sb.toString();});}
2 Redis 缓存策略
maven依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
缓存策略配置:
@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)).disableCachingNullValues() // 避免缓存穿透.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));return RedisCacheManager.builder(factory).cacheDefaults(config).build();}}
2.1 redis 连接池
Spring Boot 2.x 默认使用Lettuce作为Redis客户端。
Lettuce | 基于Netty的异步非阻塞通信;原生线程安全;长连接Lettuce性能优势更明显;CPU利用率更高效。 |
Jedis | 同步阻塞通信;非线程安全; |
表 Lettuce 与 Jedis对比
2.3 Redis 缓存问题
1 缓存穿透:查询一个根本不存在的数据,导致每次查询都会访问数据库。
使用布隆过滤器(检索元素是否存在集合中)。它内存占用小。判断元素存在有误差,但是判断元素不存在绝对准确。
2 缓存击穿:某个热点key突然实现,大量请求同时访问时,会击穿缓存直接访问数据库。
使用互斥锁或逻辑过期。也可以设置热点key永不过期或是后台刷新热点key缓存。
3 缓存雪崩:大量缓存key同时失效,大量请求直接访问数据库。发生场景有:1)缓存服务器重启;2)大量缓存设置相同过期时间。
缓存key的过期时间随机化。