👀引导
👀中文文档
👀框架介绍
名称 作用 使用场景 优点 缺点 速率 Spring Cache with Caffeine & Redis Spring Cache 抽象结合 Caffeine 和 Redis 创建二级缓存策略 Spring Boot 项目,需要简单的二级缓存解决方案 与 Spring 完美集成,配置简单 需要额外的集成工作实现二级缓存 高 (由于本地缓存) J2Cache 二级缓存策略的实现,结合了本地缓存和 Redis 需要与多种缓存提供者结合的解决方案 灵活,支持多种 L1 和 L2 缓存提供者 可能需要额外配置和调优以获得最佳性能 中到高 (取决于配置) Hazelcast 分布式数据网格解决方案,提供本地和分布式缓存 大型分布式系统,需要数据网格和缓存功能 功能丰富,支持分布式数据结构、查询和执行 相对重量级,可能过于复杂对于简单的用例 中到高 (取决于配置) LayeringCache 二级缓存策略的实现,结合了本地缓存如 Caffeine 和远程缓存如 Redis 需要灵活的二级缓存策略,结合本地和远程缓存 结合了本地和远程缓存的优势,配置灵活 需要额外的集成和配置工作 中到高 (取决于配置和网络延迟)
👀二级分布式缓存是什么
二级缓存的核心思想是将一个本地缓存和一个共享的分布式缓存结合在一起。第一级是本地缓存,提供快速访问,但其数据只在单个实例上。第二级是分布式缓存,所有应用实例共享它,但访问速度相对较慢。二级分布式缓存其实已经能够满足我们工作中的绝大部分项目,所以我没有集成更多级别的。
类别 名称 描述 特点 本地缓存 Caffeine 一个高性能、可扩展的Java 8基础的本地缓存库。通常被用作第一级缓存。 1. 基于大小、引用、到期等策略的驱逐策略。 2. 可以与LoadingCache
或AsyncLoadingCache
结合使用,实现在缓存未命中时自动加载数据。 共享的分布式缓存 Redis 一个分布式的键值存储,通常作为第二级缓存。 - Redisson 一个在Redis上实现的Java内存数据网格。 1. 为Java的各种集合、锁、信号量等提供了分布式版本。 2. 除了常规的Redis操作外。 序列化和反序列化 Jackson 一个用于处理JSON的Java库。 在缓存结构中,用于序列化和反序列化对象,以便存储到或从Redis中检索。
👀二级分布式缓存的典型工作流程
步骤 描述 步骤 1 当请求某个数据时,首先检查第一级缓存(Caffeine)。 步骤 2 如果在第一级缓存中找到数据,直接返回。 步骤 3 如果在第一级缓存中未找到数据,检查第二级缓存(Redis)。 步骤 4 如果在第二级缓存中找到数据,将其也存入第一级缓存,然后返回数据。 步骤 5 如果两级缓存都未命中,从数据源(如数据库)获取数据,然后将数据存入两级缓存。 步骤 6 在写入或更新数据时,首先更新数据源,然后使两级缓存中的相关数据失效或更新。
👀 二级分布式缓存常见问题
问题类型 问题描述 解决方案 缓存穿透 查询不存在的数据,导致频繁查询数据库 - 布隆过滤器:使用布隆过滤器判断可能存在的key。 - 空值缓存:设置空值缓存并较短过期时间。 缓存雪崩 大量缓存同时过期,导致请求转发到数据库 - 随机过期时间:为每个缓存项设置随机过期时间。 - 缓存预热:在缓存过期前预先加载数据。 - 分布式锁:使用分布式锁控制缓存加载。 缓存击穿 热点key失效,导致大量请求转发到数据库 - 永不过期策略:对于热点数据设置永不过期或手动维护。 - 互斥锁:使用锁控制并发加载数据。
👀二级分布式缓存的序列化问题
✌Caffeine和Redis
特性 Caffeine缓存 Redis缓存 存储位置 在JVM内存中,直接存储对象。 在网络中,通过序列化后的格式存储。 序列化和反序列化 不需要序列化和反序列化,对象以原始形式存储。 需要将对象序列化为可在网络上传输的格式,然后反序列化。 跨多个JVM或服务器实例的使用 不适用,Caffeine通常用于单个JVM内存中的缓存。 适用,Redis通常用于跨多个JVM或服务器实例之间的数据共享。 数据一致性和迁移 数据迁移时需要确保序列化和反序列化方式一致。 应确保序列化和反序列化方式一致,以便在不同系统之间正确共享数据。 二级缓存管理器 在二级缓存管理器中可以处理数据的序列化和反序列化。 在Redis客户端或中间件中通常处理序列化和反序列化。
✌Jackson
✍可处理的Java类型:
Java类型 处理方式 基本数据类型 无 包装类型 无 字符串和字符 无 集合类型 无 Java Beans 无 枚举类型 无 数组 无 常见的Java库对象 无 Java 8日期和时间API 使用jackson-datatype-jsr310
模块 其他Java 8+类型 使用其他Jackson模块
✍需要特殊处理或额外配置:
情况 解决方法 循环引用 使用@JsonManagedReference
、@JsonBackReference
或@JsonIdentityInfo
注解 抽象类和接口 使用@JsonTypeInfo
和相关注解 懒加载的实体 使用jackson-datatype-hibernate
模块或其他适当的模块 Java代理对象 需要特殊处理或配置以避免序列化问题 Java 8的Optional 在较早版本中可能需要额外的配置 Java 8的日期和时间API 使用jackson-datatype-jsr310
模块 自定义类型 编写自定义的序列化器和反序列化器 静态字段和transient
字段 默认情况下不会序列化,但可以自定义序列化器来处理 Java I/O类型 不适合序列化为JSON,需要特殊处理 线程、锁和其他低级并发对象 不应该被序列化,需要特殊处理或配置以排除 Java GUI组件 不应该被序列化为JSON,需要特殊处理或配置以排除
👀二级分布式缓存的实现原理
✌1. Spring Cache核心接口
名称 作用 使用原理 CacheManager
提供访问缓存的能力 作为一个中心的接口,负责管理多个缓存容器 Cache
代表缓存容器,提供基本的缓存操作 提供如 get
, put
, evict
等基本的缓存操作 @Cacheable
声明某个方法的结果是可以缓存的 在方法执行前检查是否已缓存结果,如果有则直接返回缓存的值,否则执行方法并缓存结果 @CachePut
总是导致方法被执行并将结果存入缓存 方法总是被执行,并将结果缓存或覆盖之前的缓存 @CacheEvict
在方法执行后移除缓存 方法执行后,移除指定的缓存 @Caching
允许多种缓存规则的组合 定义一个或多个 @Cacheable
, @CacheEvict
, @CachePut
的组合,以应对复杂的缓存逻辑 @CacheConfig
类级别的缓存共享配置 在类级别提供公共的缓存配置 CacheResolver
解析用于方法调用的缓存 定制如何解析使用的缓存 KeyGenerator
生成缓存键 定制如何为缓存项生成键
✌2. Spring Cache工作流程
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +
| 我的服务 |
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +
| 开始
| |
| v
| + -- -- -- -- -- -- -- -- - + 触发:服务方法被调用
| | @Caching | -- -- -- -- -- -- -- ->
| + -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- -- -- - +
| | 调用带有 @Cacheable |
| | 注解的方法 |
| + -- -- -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- -- -- - +
| | Spring AOP 代理拦截 |
| | 方法调用 |
| + -- -- -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- - + 触发:定义如TTL 、最大项数
| | CacheConfig | -- -- -- -- -- -- -- ->
| + -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- - + 触发:管理缓存实例如EhCache 、Redis
| | CacheManager | -- -- -- -- -- -- -- ->
| + -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- - + 触发:决定使用哪个缓存实例
| | CacheResolver | -- -- -- -- -- -- -- ->
| + -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- - + 触发:生成缓存键如基于方法参数
| | KeyGenerator | -- -- -- -- -- -- -- ->
| + -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- -- -- - +
| | 通过CacheManager 查 |
| | 询对应的Cache |
| + -- -- -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- -- -- -- -- - +
| | Cache 中是否存在对应数据? |
| + -- -- -- -- -- -- -- -- -- -- -- -- - +
| | |
| 是 否
| v v
| + -- -- -- -- -- -- -- -- -- -- - + + -- -- -- -- -- -- -- -- -- -- - +
| | 返回缓存中的数据 | | 调用原始方法获取数据 |
| + -- -- -- -- -- -- -- -- -- -- - + + -- -- -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- -- -- - +
| | 存储数据到Cache 中 |
| + -- -- -- -- -- -- -- -- -- -- - +
| |
| v
| + -- -- -- -- -- -- -- -- -- -- - +
| | 返回数据给调用者 |
| + -- -- -- -- -- -- -- -- -- -- - +
| |
| v
| 结束
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +
✌3. Spring Cache不支持批量缓存
Spring Framework的Cache抽象确实没有直接提供批量缓存的内置支持。Spring的缓存注解(如@Cacheable、@CachePut、@CacheEvict等)主要用于单个方法调用的缓存管理,而不是针对一组方法的批量缓存。
Spring缓存注解 描述 触发缓存接口方法 @Cacheable
用于标记方法,表示方法的结果应该被缓存,当相同的参数被传递给方法时,缓存会被命中并返回结果。 触发 get
操作和 put
操作 @CachePut
用于标记方法,表示方法的结果应该被缓存,但不论何时调用,都会强制更新缓存。 触发 put
操作 @CacheEvict
用于标记方法,表示方法的结果应该从缓存中移除,通常在方法执行后调用,以清除缓存中的数据。 触发 evict
操作
原有的@Cacheable
,@CachePut
和@CacheEvict
自定义@BatchCacheable
, @BatchCachePut
, 和 @BatchCacheEvict
✌4. Spring Cache同时配置Redis和Redisson
Redis和Redisson配置 注意事项 Redis配置 - 需要确保与Redis服务器连接的信息正确匹配,包括主机地址、端口、密码等。 - 应根据应用程序的需求配置Redis的数据持久化选项,以确保数据一致性和持久化需求得到满足。 Redisson配置 - Redisson配置通常包括连接到Redis服务器的信息,确保与Redis配置一致。 - Redisson还包括对分布式数据结构和锁等功能的配置,应根据应用程序需求进行调整。 关系和注意事项 - Redis和Redisson通常可以和谐共存,它们在不同层面上与Redis进行交互,不会直接冲突。 - 在配置过程中,确保Redis和Redisson的配置信息准确、一致,以避免潜在问题。 - 确保应用程序正确使用Redis和Redisson提供的功能和API,以充分利用它们的优势。
👀 二级分布式缓存的实现方案
✌需求分析
缓存层级 缓存实现 策略说明 一级缓存 Caffeine 一级缓存通常是应用程序内部的内存缓存,用于快速读取和写入数据,提高访问速度。 二级缓存 Redis 二级缓存通常是分布式缓存,用于在多个服务或节点之间共享数据,确保数据的一致性和可靠性。 多服务 Redisson 分布式锁 对于多服务环境,使用 Redisson 客户端提供的分布式锁实现。这可以确保在分布式环境中的线程安全。 单服务 双重检查锁策略 对于单服务环境,使用双重检查锁策略实现线程安全,以避免多个线程同时加载同一个键的数据。
✌配置分析
模式 Redis 配置 Redisson 配置 原因 单节点模式 在 yml
中配置单节点 Redis 信息 在 yml
中配置 Redisson 和单节点 Redis 信息,配合代码配置其他功能 单节点模式相对简单,只需配置单个 Redis 服务器。但 Redisson 可以提供额外的分布式功能,需要在代码中配置。 哨兵模式 在 yml
中配置哨兵 Redis 信息 在 yml
中配置 Redisson 和哨兵 Redis 信息,配合代码配置其他功能 哨兵模式提供了高可用性,需要在配置文件中指定哨兵节点。 Redisson 可以在代码中配置分布式功能。 集群模式 在 yml
中配置 Redis 集群信息 在 yml
中配置 Redisson 和 Redis 集群信息,配合代码配置其他功能 集群模式提供了数据分片和高可用性,需要指定多个节点。 Redisson 可以在代码中配置其他功能。
✌序列化分析
技术 是否需要序列化 序列化建议 应用场景 Java代码配置 Caffeine 否 不适用 内存中的数据缓存 不适用 Redis 是 序列化为二进制或其他常见格式 缓存、数据存储、消息队列、分布式锁等 使用Spring Boot的RedisTemplate
,序列化工具为GenericJackson2JsonRedisSerializer
Redisson 是 序列化为Java对象,确保可序列化 分布式集合、分布式锁、分布式对象、分布式计算等 使用Redisson的Config
。要在Redisson的GenericJackson2JsonCodec extends BaseCodec
中使用GenericJackson2JsonRedisSerializer
的配置,可以考虑从后者获取ObjectMapper
并将其传递给前者。