JAVA项目中使用Caffeine和Redis实现二级(多级)缓存

JAVA项目中使用Caffeine和Redis实现二级(多级)缓存

1. 什么是多级缓存?适合什么样的业务场景?

1.1 什么是多级缓存?

多级缓存指在系统中同时使用不同层级、不同类型的缓存,一般分为:

层级 名称 位置 特点
一级缓存(L1) 本地缓存,如 Caffeine 应用 JVM 内存中 极快(纳秒级)、无网络开销、容量有限、无法跨实例共享
二级缓存(L2) 分布式缓存,如 Redis 独立缓存服务器 稳定共享、容量大、需要网络通信、延迟高于本地缓存

多级缓存 = L1(极快) + L2(共享)优势互补

  • 一级缓存(本地缓存):直接与应用程序关联,适合频繁访问的数据。
  • 二级缓存(远程缓存):作为一级缓存的补充,存储相对较不常访问的数据。

1.2 使用二级缓存(Caffeine + Redis)的优势

用最小成本获得最高性能,并保证缓存的数据一致性和高可用性。

1
2
应用请求 → 一级缓存(本地) → 二级缓存(分布式) → 数据库
↑ 命中率95% ↑ 命中率4% ↑ 访问1%

A. 性能优势 —— 延迟更低,吞吐量更高

  1. L1 本地缓存(Caffeine)—— 纳秒级读取
  2. L2 分布式缓存(Redis)—— 毫秒级共享访问
    • 当 L1 未命中,L2 仍能快速响应
    • 缓解数据库压力

多级缓存 = 用 L1 提高速度,用 L2 兜底和共享

B. 稳定性优势 —— 降低 Redis 压力、防雪崩

  1. 防止 Redis 被热点击穿
    • 大量访问直接在 L1 缓存中完成,不打 Redis
  2. Redis 故障时系统仍可降级运行
    • Redis 挂掉后:L1 缓存仍然有效, 系统不会立即雪崩式崩溃

C. 兼具高性能与一致性

  1. L1 缓存解决速度瓶颈
  2. L2 缓存提供跨节点一致数据

1.3 什么业务场景适合Caffeine?

适合的业务场景:

  • Caffeine 适合需要快速访问、短期存储的数据场景,如频繁查询的热点数据、计算结果缓存等,尤其是在高并发环境下表现优异。

不适合的业务场景

  • Caffeine不适合实时性要求高或数据变更频繁的场景
  • Caffeine也不适合需要强一致性保证的数据存储,因为它主要关注性能和命中率,而不是数据的一致性。

2. JAVA项目中如何实现 Caffeine + Redis 的二级缓存体系

项目地址

2.0 前提

  • 你的项目已经形成了初步的微服务架构

2.1 总体结构和查询流程

利用 Caffeine 构建本地高速缓存作为 L1,Redis 构建分布式缓存作为 L2,并通过自定义注解 + AOP + 自动装配,实现了高性能、低延迟、可控的一致性缓存架构,各微服务只需引入 aimin-cache 模块即可无侵入式使用二级缓存。

  • aimin-cache模块

查询流程

  1. 先查本地 Caffeine
  2. 本地没有 → 查询 Redis
  3. Redis 没有 → 查询数据库
  4. 设置 Redis + Caffeine 双缓存
  5. 更新数据时同步清除两级缓存

写数据流程

  • update DB → 删除 Redis → 删除 Caffeine

优先删除Redis

  • 避免 Redis 仍保留旧数据
  • 先清除redis中缓存数据,然后清除caffeine中的缓存,避免短时间内如果先清除caffeine缓存后其他请求会再从redis里加载到caffeine中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
         业务方法调用


┌────────────────┐
│ L2CacheAspect │ ← 切面拦截
└────────────────┘

查询类型(QUERY)?


┌───────────────┐
│ L1 Caffeine │
└───────────────┘
MISS │ │ HIT
▼ ▼
┌───────────────────────┐
│ L2 Redis (JSON) │
└───────────────────────┘
MISS │ │ HIT
▼ ▼(回填 L1)
DB 查询 返回结果


写入 L1 + 写入 L2


返回结果

2.2 引入依赖

在aimin-cache模块的pom.xml文件中引入以下依赖:

依赖 作用
spring-boot-starter-cache 启用 Spring Cache 框架(Cacheable/CacheEvict)
spring-boot-starter-aop 启用 AOP(用于你项目的自定义缓存注解切面)
caffeine L1 本地高速缓存
aimin-redis Redis 客户端封装,用于 L2 缓存
hutool-jsonhutool-crypto 处理缓存值 JSON 序列化、加解密
mybatis-plus 用于 BaseCacheService 需要调用数据库时使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>

<dependency>
<groupId>com.oimc</groupId>
<artifactId>aimin-redis</artifactId>
</dependency>

2.3 对缓存的一些配置 cache.yml

  • 总体结构配置
  • 一级缓存(Caffeine)配置
  • 二级缓存(Redis)配置
  • l2cache(Caffeine 二级本地缓存,用于 Redis 前置)配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
aimin-cache:
config:
# 是否存储空值,默认true,防止缓存穿透
allowNullValues: true
dynamic: true
# 组合缓存配置
composite:
# 是否全部启用一级缓存,默认false
L1EnabledGlobally: true
# 一级缓存
caffeine:

# 二级缓存
redis:

l2cache:
initial-capacity: 1000
maximum-size: 10000
expire-after-write: 60
time-unit: SECONDS

2.4 aimin-cache 整体模块

  • 两套二级缓存体系
  • 一个“轻量级简单版”和一个“企业级正式版”
分支 包路径 用处
simple 版 com.oimc.aimin.cache.simple.* 通过注解 @L2Cache + AOP 实现二级缓存(简单且易用)
formal 版 com.oimc.aimin.cache.formal.* 完整企业级二级缓存体系(CacheManager + Caffeine + Redis + 消息同步)

2.4.1 simple 版(注解式)二级缓存 — 适合业务层快速使用

simple 版由以下核心类组成:

  • L2Cache 注解(定义缓存规则)
  • L2CacheAspect AOP 切面(实现二级缓存逻辑)
  • SimpleCacheProperties(配置 Caffeine)
  • L2CacheConfig(创建 Caffeine + 配置)
  • ElParser(解析 SpEL,生成缓存 key)

这套是 “AOP 自动织入 + Caffeine + RedisTemplate” 形式。

2.4.2 formal版二级缓存 — Spring Cache + CacheManager + 消息同步

包括:

  • AiminCacheAutoConfiguration自动配置类(装配整个缓存体系)
  • AiminCacheManager(创建缓存实例)
  • AiminCache(整合 Caffeine + Redis 的实际缓存实现)
  • L2RedisCache(Redis 操作封装)
  • CacheMessageListener(Redis pub/sub 清除本地缓存)
  • BaseCacheService(业务使用封装)

2.5 详细分析aimin-cache-simple包中的各个类

2.5.1 @L2Cache 注解(定义二级缓存行为)

提供:

  • cacheName:缓存名称
  • key:支持 SpEL 的动态 key
  • type:CacheType(QUERY/PUT/DELETE 等)

特点:

  • 查询顺序:Caffeine → Redis → DB
  • 更新顺序:DB → Redis → Caffeine
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 二级缓存注解,用于标记需要进行缓存的方法
* 通过AOP实现,支持Caffeine本地缓存和Redis分布式缓存的二级缓存结构
* 查询顺序:Caffeine -> Redis -> 数据库
* 更新策略:数据库 -> Redis -> Caffeine
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableAspectJAutoProxy
public @interface L2Cache {

/**
* 缓存名称,用于标识缓存的业务类型
* 默认值为"aimin-cache"
*/
String cacheName() default "aimin-cache";

/**
* 缓存key,支持SpEL表达式
* 用于确定缓存的唯一标识,通常根据方法参数构建
* 示例:#user.id 表示使用user参数的id属性作为key
*/
String key();

/**
* Redis缓存过期时间,单位秒
* 默认为120秒
*/
long l2TimeOut() default 120;

/**
* 缓存操作类型
* FULL: 查询并缓存数据(默认)
* PUT: 仅更新缓存,不查询已有缓存
* DELETE: 删除指定的缓存内容
*/
CacheType type() default CacheType.FULL;
}

2.5.2 L2CacheAspect — 缓存 AOP 实现(核心逻辑)

这是 AOP + 注解方式的二级缓存

  • (1) 拦截带 @L2Cache 的方法
1
@Pointcut("@annotation(com.oimc.aimin.cache.simple.annotation.L2Cache)")
  • (2) 解析 key(SpEL)
1
2
String elResult = ElParser.parse(annotation.key(), treeMap);
String realKey = annotation.cacheName() + "::" + elResult;
  • (3) QUERY 类型:两级缓存查询顺序
1
2
3
4
5
6
7
if (annotation.type() == CacheType.QUERY) {
// 1. 查 L1 Caffeine
Object o = cache.getIfPresent(realKey);
// 2. 查 L2 Redis
Object redisValue = redisTemplate.opsForValue().get(realKey);
// 3. 回填 L1
}
  • (4) PUT 类型:写入两级缓存
1
2
3
Object object = point.proceed();
redisTemplate.opsForValue().set(realKey, object);
cache.put(realKey, object);
  • (5) DELETE 类型:删除两级缓存
1
2
redisTemplate.delete(realKey);
cache.invalidate(realKey);

2.5.3 config

  • CacheType: 缓存操作类型枚举:定义 @L2Cache 的工作模式
  • SimpleCacheProperties —— simple 体系下的 Caffeine 配置
  • L2CacheConfig —— 创建 simple 版二级缓存所需的 Caffeine 实例
1
2
3
4
5
6
7
cache.yml (l2cache.* 配置)

SimpleCacheProperties
↓ (注入配置)
L2CacheConfig → 创建 Caffeine 缓存 Bean

L2CacheAspect (使用 CacheType 决定缓存行为)
  • CacheType 决定缓存操作行为
  • SimpleCacheProperties 读取并处理配置文件参数
  • L2CacheConfig 根据这些参数创建 Caffeine 缓存实例,供 AOP 切面(L2CacheAspect)使用。

一、CacheType(缓存操作类型枚举)—— 定义 @L2Cache 的工作模式

这个枚举表示「调用 @L2Cache 注解时,缓存要怎么工作」。
它是 缓存使用策略的枚举类型,对应 AOP 切面 L2CacheAspect 的不同执行分支。

① FULL —— 完整查询模式(默认)

1
FULL

查询顺序:

Caffeine(L1)→ Redis(L2)→ 方法本体(DB)→ 更新缓存

这是最典型的二级缓存逻辑,适合查询方法:

1
2
@L2Cache(cacheName="user", key="#id", type=CacheType.FULL)
public User getById(Long id) { ... }

特点:

  • 会先查缓存,不命中才查数据库。
  • 查到的数据会写入两级缓存。

② PUT —— 强制更新缓存模式

1
PUT

特点:

  • 不查缓存,直接执行方法体(通常是 DB 查询/更新)。
  • 把新的数据写入 L1 + L2 缓存。

用途:
当业务要确保缓存最新时:

1
2
@L2Cache(type = CacheType.PUT)
public void updateUser(...)

③ DELETE —— 删除缓存模式

1
DELETE

用途:

  • 用于删除某个缓存键
  • 或清空整个缓存(根据 key & cacheName)

例子:

1
2
@L2Cache(type = CacheType.DELETE)
public void deleteUser(int id) { ... }

二、SimpleCacheProperties —— simple 体系下的 Caffeine 配置

这个类是 读取配置文件(cache.yml → l2cache.*)的属性载体(ConfigurationProperties)

对应你 yml 里这段:

1
2
3
4
5
l2cache:
initial-capacity: 1000
maximum-size: 10000
expire-after-write: 60
time-unit: SECONDS

它的功能是:

把 yml 中 Caffeine 缓存的配置读取出来,并转换为 Java 能使用的参数。

三、L2CacheConfig —— 创建 simple 版二级缓存所需的 Caffeine 实例

这个类是 simple 模式的核心配置类:

根据 SimpleCacheProperties 创建一个 Caffeine 缓存 Bean(一级缓存 L1)。

同时:

  • 标记 @EnableCaching → 启用 Spring Cache
  • 让 AOP + 缓存注解可以工作
核心方法:创建 Caffeine Cache Bean
1
2
3
4
5
6
7
8
9
@Bean
public Cache<String, Object> caffeineCacheManager() {
return Caffeine.newBuilder()
.initialCapacity(l2CacheProperties.getInitialCapacity())
.maximumSize(1000)
.expireAfterWrite(l2CacheProperties.getExpireAfterWrite(),
l2CacheProperties.getExpireAfterWriteTimeUnit())
.build();
}

作用:

  • 创建一个 全局唯一 的 Caffeine 本地缓存实例
  • 作为 simple 模式的一级缓存
  • 被 L2CacheAspect 注入,用于缓存查询/写入/删除

2.5.4 ElParser

ElParser 用于解析 @L2Cache 注解中的 SpEL(Spring Expression Language)表达式,从方法参数生成最终的缓存 Key。

它让你可以写:

1
2
@L2Cache(cacheName = "user", key = "#id")
public User getUser(Long id)

而程序能自动得到真实缓存 key:

1
user::1001

2.6 详细分析aimin-cache-formal包中的各个类

  • AiminCacheAutoConfiguration自动配置类(装配整个缓存体系)
  • AiminCacheManager(创建缓存实例)
  • AiminCache(整合 Caffeine + Redis 的实际缓存实现)
  • L2RedisCache(Redis 操作封装)
  • CacheMessageListener(Redis pub/sub 清除本地缓存)
  • BaseCacheService(业务使用封装)

2.6.1 AiminCacheAutoConfiguration — 自动装配入口

只要你的微服务依赖 aimin-cache,Spring Boot 会自动启动二级缓存体系

  • (1) 创建 Redis 二级缓存工具类 L2RedisCache
1
2
@Bean
public L2RedisCache redisCache(RedisTemplate redisTemplate)
  • (2) 创建 AiminCacheManager(整个缓存体系的中心)
1
2
@Bean
public AiminCacheManager cacheManager(L2RedisCache l2RedisCache)
  • (3) 创建 Redis 监听器,用于多节点同步缓存
1
2
3
redisMessageListenerContainer.addMessageListener(
new CacheMessageListener(l2RedisCache, cacheManager)
)

2.6.2 AiminCacheManager — 负责创建每个缓存

getCache(name):创建/获取缓存实例

1
cache = new AiminCache(name, redisCache, caffeineCache(), aiminCacheConfig)

它实际返回的 Cache 是:

  • Caffeine 作为 L1
  • Redis 作为 L2
  • 包装成 AiminCache

AiminCacheManager 内部不仅管理缓存实例,还负责:

  • dynamic = true 时允许动态创建 cacheName
  • 使用自定义 Caffeine 配置
    详见 caffeineCache() 方法

2.6.3 AiminCache — 真正的二级缓存实现(核心中的核心)

它继承 Spring 的 AbstractValueAdaptingCache

成员组成

成员 作用 示例代码
L1:Caffeine 本地高速缓存 private Cache<Object,Object> l1CaffeineCache
L2:Redis 分布式缓存 private L2RedisCache l2RedisCache
配置 控制缓存策略 AiminCacheConfig caffeine/redis/composite
加锁机制 解决缓存击穿 ReentrantLock[] lockArray

⭐ lookup() 方法:二级缓存查询核心

1
2
3
4
5
6
7
8
Object value = l1CaffeineCache.getIfPresent(key);
if(value != null) return value;

// Redis
value = l2RedisCache.get(cacheKey);

// 回填
if(value != null) l1CaffeineCache.put(key, value);

二级缓存读取顺序:
Caffeine → Redis → DB,然后回填 Caffeine

⭐ put() 方法:写入顺序

  • 写入 Redis(以 ms 为单位)
  • 写入 Caffeine
  • 发布同步消息(用于集群同步)

⭐ clearLocal():用于集群同步

2.6.4 L2RedisCache — Redis 缓存层封装

支持:

  • set(key, value, timeout)
  • get(key)
  • hashAdd
  • deleteKeys

RedisTemplate 由自动配置注入。

2.6.5 CacheMessageListener — 集群缓存同步

Redis 发布/订阅 pub/sub

收到消息 → 清除本地节点的 L1 Caffeine 缓存:
让多个服务节点保持一致。

1
aiminCacheManager.clearLocal(cacheName, key);

2.6.6 BaseCacheService — 给业务层使用的统一封装

业务层真正使用的入口:

1
Cache cache = getCacheManager().getCache(getCacheName());

负责:

  • 自动生成 Key(使用 MD5) → keyGen()
  • 自动获取缓存(cache.get)
  • 不命中 → 走数据库 → 写入缓存

2.7 跨微服务使用:其他微服务如何使用二级缓存?

2.7.1 微服务只需引入aimin-cache模块,即可自动开启二级缓存

aimin-admin的启动类添加注解

  • @SpringBootApplication
  • @EnableCaching
1
2
3
4
5
6
7
8
9
10
@SpringBootApplication
@MapperScan("com.oimc.aimin.admin.mapper")
@EnableCaching
@EnableFeignClients
public class AiminAdminApplication {
public static void main(String[] args) {
ConfigurableApplicationContext app = SpringApplication.run(AiminAdminApplication.class, args);
BeanManager.setAppContext(app);
}
}

@SpringBootApplication注解会自动导入AiminCacheAutoConfiguration

  • 自动加载配置cache.yml
  • 自动注册 CacheManager
  • 自动注册切面 L2CacheAspect
  • 自动开启缓存

2.7.2 业务调用方式(两种)

simple版:直接使用自定义注解

1
2
3
4
@L2Cache(cacheName="adminRole", key="#roleId", type=CacheType.QUERY)
public AdminRole getRole(Long roleId) {
return adminRoleMapper.selectById(roleId);
}

formal版:BaseCacheService

1
2
3
public class DrugService implements BaseCacheService<Drug> {
String getCacheName() { return "drug"; }
}

然后直接使用:

1
cacheGetById(id);

END