首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >​架构师必看:Java时代Redis二级缓存的正确打开方式

​架构师必看:Java时代Redis二级缓存的正确打开方式

原创
作者头像
李福春
发布2026-06-16 11:25:02
发布2026-06-16 11:25:02
1201
举报

本文已配套 5 个可本地运行或可验证的工程,均放在当前目录同级子目录:

jetcache-direct-demo:直接使用 JetCache 的成熟框架方案。 l2cache-starter-demo:抽取一个只保留二级缓存能力的轻量 Spring Boot Starter。 springboot4-l2cache-demo:Spring Boot 4.1.0 + Java 17 的手工二级缓存示例。 layering-cache-boot4-probe:Spring Boot 4.1.0 + Java 17 + Layering Cache 兼容性探针。 boot4-internal-l2cache-demo:Spring Boot 4.1.0 + Java 17 的内部二级缓存 starter,对比 JetCache/Layering Cache 迁移。


一、痛点引入:老李的高并发噩梦

老李负责一个商品详情服务。大促前压测时,商品详情页 QPS 到 5 万,每次请求都要走远端 Redis,再在未命中时打 MySQL。Redis 本身很快,但网络 RTT、连接池、序列化和热点 key 抖动叠在一起,P99 仍然不够稳。

真正的问题不是“Redis 慢”,而是热点读路径太长:

二级缓存的目标很直接:把最热的数据放到 JVM 内存 L1,把跨节点共享的数据放到 Redis L2。命中 L1 时少一次网络;命中 L2 时少一次数据库;都未命中时再回源,并把结果回填两级缓存。


二、二级缓存是什么

常见组合是:

  • L1:Caffeine,本地堆内缓存,微秒级,单进程可见。
  • L2:Redis,分布式缓存,毫秒级,多节点共享。
  • Loader:数据库、HTTP API、向量库、配置中心等真实数据源。

二级缓存不是把 Redis 替换掉,而是在 Redis 前面加一层本地热点保护。


三、读路径与写路径

读路径要坚持“先近后远、命中回填、失败降级”:

写路径要先保证数据库正确,再处理缓存。多数业务优先选择“写 DB 后删除缓存”,用下一次读重建缓存:

一致性策略可以按业务风险选择:


四、主流方案与资料地址

JetCache

JetCache 中文文档说明它提供统一 API 和注解,支持 TTL、两级缓存、分布式自动刷新、统计、Caffeine 与 Redis 等实现。2026-06-15 重新校验 Maven metadata 后,jetcache-starter-redis-lettuce 的 latest/release 为 2.8.0.RC,但本文直接示例使用已经本地验证通过的稳定线 2.7.9

关于 Spring Boot 4:截至 2026-06-16,JetCache 中文文档只声明 JetCache 2.8+ 需要 JDK17、Spring Framework 6.x、Spring Boot 3.x,未声明 Spring Framework 7 / Spring Boot 4 支持;GitHub 上也已有 jetcache-starter-redis-lettuce 什么时候能支持 springboot 4.x 的开放 issue。因此本文不把 JetCache 写成 Boot 4 已支持方案,而是给出内部 starter 迁移样例。

Layering Cache

Layering Cache 是竞品方案,Maven metadata 显示 latest/release 为 3.5.0。它更贴近 Spring Cache 注解模型,适合团队已有 Spring Cache 使用习惯、希望以较低迁移成本进入二级缓存的场景。

Layering Cache README 描述它使用 Caffeine 做一级缓存、Redis 做二级缓存;一致性通过“推拉结合”处理,推基于 Redis Pub/Sub,拉基于消息队列与消费偏移。本文新增 layering-cache-boot4-probe,用 Boot 4.1.0 做兼容性验证。

Spring Boot 4

官方系统要求页显示:Spring Boot 4.1.0 至少需要 Java 17,并要求 Spring Framework 7.0.8 或以上。本文的 Boot 4 示例使用 Java 17,避免把尚未验证兼容的 JetCache 强行塞进 Boot 4。

Spring Data Redis 官方文档把 Pub/Sub 分为发布和订阅两类操作,并提供 RedisMessageListenerContainer 来管理订阅连接;本文内部 starter 的分布式 L1 失效广播就基于这个能力实现。


五、选型矩阵

维度

JetCache 直接使用

轻量 starter

Layering Cache 探针

手写 Boot 4 示例

Boot4 内部 starter

核心目标

成熟生产框架

只保留 L1 + L2

验证竞品在 Boot4 下能否落地

验证 Boot 4 + Java 17

JetCache 暂无 Boot4 声明时的内部迁移样例

本文工程

jetcache-direct-demo

l2cache-starter-demo

layering-cache-boot4-probe

springboot4-l2cache-demo

boot4-internal-l2cache-demo

注解能力

丰富

@L2Cached@L2CacheEvict

Layering Cache 注解

无框架注解

@InternalL2Cached@InternalL2CacheEvict

自动刷新

支持

不支持

框架声明支持

不支持

不支持,保持最小能力边界

分布式失效

框架支持

不内置

推拉结合

不内置

Redis Pub/Sub 广播 L1 失效

序列化

多策略

Jackson 示例

Kryo/FastJson/Jackson/JDK/Protostuff

JDK 序列化示例

KryoRedisSerializer

学习成本

低到中

依赖面

较大

适合场景

Boot3 生产通用框架

团队想做内部 starter

竞品评估

Boot 4 技术预研

Boot4 迁移与内部可控能力

选型流程建议:


七、本地工程一:JetCache 直接使用

目录:20260615/jetcache-direct-demo

技术栈:

  • Spring Boot 3.5.15
  • Java 17
  • JetCache 2.7.9
  • Redis Lettuce

核心代码在 ProductService

代码语言:java
复制
@Cached(
    name = "jetcache-direct-demo:product:",
    key = "#id",
    expire = 300,
    localExpire = 60,
    cacheType = CacheType.BOTH
)
public Product getProduct(Long id) {
    return loadFromDatabase(id);
}

工程结构:

本地验证:

代码语言:powershell
复制
cd D:\src\codeup\docsync\20260615\jetcache-direct-demo
$env:JAVA_HOME='C:\Users\Carter.li\scoop\apps\temurin17-jdk\current'
$env:Path="$env:JAVA_HOME\bin;$env:Path"
mvn test
mvn package -DskipTests
docker compose up -d redis
java -jar target\jetcache-direct-demo-1.0.0.jar

接口验证:

代码语言:powershell
复制
curl.exe -s http://localhost:18081/api/products/1
curl.exe -s http://localhost:18081/api/products/1
curl.exe -s -X DELETE http://localhost:18081/api/products/1/cache

本地验收结果:

步骤

关键结果

第一次查询

dbLoads=1elapsedNanos=375547500

第二次查询

dbLoads=1elapsedNanos=4292200

删除后查询

dbLoads=2elapsedNanos=71954900


八、本地工程二:轻量 Spring Boot Starter

目录:20260615/l2cache-starter-demo

这个工程按用户要求从大框架里抽出一个轻量 starter,只保留二级缓存相关能力,不追求覆盖 JetCache 的全部功能。

模块:

  • l2cache-spring-boot-starter:starter 本体。
  • l2cache-demo-app:消费 starter 的示例应用。

能力边界:

只保留:

  • @L2Cached
  • @L2CacheEvict
  • L2CacheMode.LOCAL
  • L2CacheMode.REMOTE
  • L2CacheMode.BOTH
  • Caffeine L1
  • Redis L2
  • SpEL key
  • 命中与淘汰统计

刻意不做:

  • 自动刷新
  • 分布式锁
  • Pub/Sub 广播失效
  • 多 area、多 CacheManager
  • 复杂序列化治理

与 JetCache 的架构对比:

本地验证:

代码语言:powershell
复制
cd D:\src\codeup\docsync\20260615\l2cache-starter-demo
$env:JAVA_HOME='C:\Users\Carter.li\scoop\apps\temurin17-jdk\current'
$env:Path="$env:JAVA_HOME\bin;$env:Path"
mvn test
mvn test package -DskipTests=false
docker compose up -d redis
java -jar l2cache-demo-app\target\l2cache-demo-app-1.0.0.jar

接口验证:

代码语言:powershell
复制
curl.exe -s http://localhost:18082/api/products/1
curl.exe -s http://localhost:18082/api/products/1
curl.exe -s -X DELETE http://localhost:18082/api/products/1/cache

本地验收结果:

步骤

关键结果

第一次查询

dbLoads=1stats.misses=1stats.puts=1

第二次查询

dbLoads=1stats.localHits=1

删除后查询

dbLoads=2stats.evictions=1stats.puts=2


九、本地工程三:Spring Boot 4 + Java 17

目录:20260615/springboot4-l2cache-demo

技术栈:

  • Spring Boot 4.1.0
  • Spring Framework 7.0.8
  • Java 17
  • Caffeine
  • Spring Data Redis

为什么不在 Boot 4 示例里直接上 JetCache:

本地验证过程中也踩到了这个迁移点:首次使用 GenericJackson2JsonRedisSerializer 启动 Boot 4 应用时,因 Jackson 2/3 类型不兼容报 TypeResolverBuilder 缺失。最终示例改为 JdkSerializationRedisSerializer,让文章聚焦二级缓存读写路径。生产如果需要 JSON,应专门设计 Jackson 3 兼容序列化策略。

核心代码:

代码语言:java
复制
public Product get(Long id, Supplier<Product> loader) {
    Product local = localCache.getIfPresent(id);
    if (local != null) {
        localHits.incrementAndGet();
        return local;
    }

    Product remote = redisTemplate.opsForValue().get(remoteKey(id));
    if (remote != null) {
        remoteHits.incrementAndGet();
        localCache.put(id, remote);
        return remote;
    }

    misses.incrementAndGet();
    Product loaded = loader.get();
    if (loaded != null) {
        localCache.put(id, loaded);
        redisTemplate.opsForValue().set(remoteKey(id), loaded, REMOTE_TTL);
    }
    return loaded;
}

本地验证:

代码语言:powershell
复制
cd D:\src\codeup\docsync\20260615\springboot4-l2cache-demo
$env:JAVA_HOME='C:\Users\Carter.li\scoop\apps\temurin17-jdk\current'
$env:Path="$env:JAVA_HOME\bin;$env:Path"
mvn test
mvn package -DskipTests
docker compose up -d redis
java -jar target\springboot4-l2cache-demo-1.0.0.jar

接口验证:

代码语言:powershell
复制
curl.exe -s http://localhost:18083/actuator/health
curl.exe -s http://localhost:18083/api/products/1
curl.exe -s http://localhost:18083/api/products/1
curl.exe -s -X DELETE http://localhost:18083/api/products/1/cache

本地验收结果:

步骤

关键结果

健康检查

status=UP

第一次查询

dbLoads=1stats.misses=1elapsedNanos=232308500

第二次查询

dbLoads=1stats.localHits=1elapsedNanos=72400

删除后查询

dbLoads=2stats.evictions=1stats.misses=2


十、本地工程四:Layering Cache + Spring Boot 4 兼容性探针

目录:20260615/layering-cache-boot4-probe

技术栈:

  • Spring Boot 4.1.0
  • Java 17
  • Layering Cache 3.5.0
  • Caffeine
  • Redis Lettuce
  • Kryo 序列化

这个工程不是在宣称 Layering Cache 官方已经支持 Boot 4,而是把兼容性风险显性化。实测过程里,原始依赖可以通过 Maven 编译;运行时需要额外补 aspectjweaverkryofastjson 后应用才能启动。随后又发现默认 Kryo 序列化对包含 BigDecimal 的 record 会报 Class is not registered,因此探针模型改成 long priceCents,把关注点收敛到 Boot4、注解切面和二级缓存路径。

核心注解示例:

代码语言:java
复制
@Cacheable(
    cacheNames = "layering-cache-boot4-probe:product",
    key = "#id",
    cacheMode = CacheMode.ALL,
    firstCache = @FirstCache(expireTime = 60, timeUnit = TimeUnit.SECONDS),
    secondaryCache = @SecondaryCache(expireTime = 300, timeUnit = TimeUnit.SECONDS)
)
public Product getProduct(Long id) {
    return loadFromDatabase(id);
}

兼容性探针流程:

本地验证命令:

代码语言:powershell
复制
cd D:\src\codeup\docsync\20260615\layering-cache-boot4-probe
$env:JAVA_HOME='C:\Users\Carter.li\scoop\apps\temurin17-jdk\current'
$env:Path="$env:JAVA_HOME\bin;$env:Path"
mvn test package -DskipTests=false
java -jar target\layering-cache-boot4-probe-1.0.0.jar --server.port=18084

验收结论:

检查项

结果

Maven 测试与打包

通过,Tests run: 1, Failures: 0, Errors: 0

原始运行时依赖

不完整,依次暴露 AspectJ、Kryo、FastJson 缺失

补依赖后启动

通过,/actuator/health 返回 UP

Kryo 复杂对象

BigDecimal record 失败,需要注册或自定义序列化策略

二级缓存命中

简化模型可返回接口,但未作为 Boot4 生产推荐方案

架构判断:Layering Cache 可以作为竞品和迁移参考,但在 Boot 4 上不宜直接认定为生产可用。若团队要用,需要先把依赖传递、序列化注册、Boot 4/Spring 7 自动配置和多节点一致性全部纳入准入测试。


十一、本地工程五:Boot4 内部二级缓存 starter

目录:20260615/boot4-internal-l2cache-demo

模块:

  • internal-l2cache-spring-boot-starter:内部 starter 本体。
  • internal-l2cache-demo-app:业务示例应用。

能力边界:

  • L1:Caffeine。
  • L2:Redis。
  • 注解:@InternalL2Cached@InternalL2CacheEvict
  • Key:SpEL。
  • 序列化:KryoRedisSerializer
  • 一致性:写后删除 Redis,并通过 Redis Pub/Sub 广播删除其他节点 L1。
  • 统计:localHitsremoteHitsmissesputsevictionsinvalidations

内部框架结构:

读路径:

分布式一致性:

为什么不用 JetCache 直接搬到 Boot4:

本地验证命令:

代码语言:powershell
复制
cd D:\src\codeup\docsync\20260615\boot4-internal-l2cache-demo
$env:JAVA_HOME='C:\Users\Carter.li\scoop\apps\temurin17-jdk\current'
$env:Path="$env:JAVA_HOME\bin;$env:Path"
mvn test package -DskipTests=false
docker compose up -d redis

双节点验证:

代码语言:powershell
复制
java -jar internal-l2cache-demo-app\target\internal-l2cache-demo-app-1.0.0.jar --server.port=18085 --internal-l2cache.instance-id=node-a
java -jar internal-l2cache-demo-app\target\internal-l2cache-demo-app-1.0.0.jar --server.port=18086 --internal-l2cache.instance-id=node-b

实测结果:

步骤

关键证据

Maven 测试与打包

BUILD SUCCESS,starter 3 个测试、demo 1 个测试通过

节点健康检查

1808518086 均返回 status=UP

B 节点第一次读

misses=1puts=1dbLoads=1

B 节点第二次读

localHits=1dbLoads=1

A 节点更新

返回 name=node-a-updatedversion=2evictions=1

B 节点更新后读取

返回 version=2invalidations=1,证明 B 的 L1 被 Pub/Sub 清理


十三、缓存风险与排障图

二级缓存的风险不在“会不会命中”,而在“命中错了怎么办”。

上线前建议检查:

风险

检查点

建议

L1 旧值

多节点更新后另一个节点是否读旧值

对强一致数据缩短 L1 TTL 或使用广播失效

缓存穿透

不存在 id 是否反复打 DB

对 null 值设置短 TTL

缓存击穿

热 key 过期瞬间是否大量回源

加互斥加载或自动刷新

缓存雪崩

大量 key 是否同一秒过期

TTL 加随机抖动

序列化迁移

Boot 4/Jackson 3 是否兼容

独立做序列化回归,不混在业务改造里


十四、给 AI Coding 的落地提示词

把缓存设计交给 AI 时,不要只问“帮我加缓存”,要给读写比、一致性要求、失效策略和验收方式:

你是 Java 缓存架构顾问。

请基于以下 Service 和 Repository 代码设计 Java 17 + Redis 二级缓存方案。

要求:

  1. 判断是否适合 L1 Caffeine + L2 Redis。
  2. 给出 cache name、key、L1 TTL、L2 TTL。
  3. 写出 JetCache 方案和轻量 starter 方案各一份。
  4. 说明写后删除、写后更新、自动刷新三种策略取舍。
  5. 给出 docker compose、mvn test、curl 验收命令。
  6. 输出 Mermaid 图说明读路径、写路径和故障排查。

AI 生成之后,按下面的验收闭环收口:


十五、最终建议

结论:

  1. 如果团队要快速生产落地,优先用 JetCache 稳定线,先在 Spring Boot 3 + Java 17 场景跑通。
  2. 如果只需要 L1 + L2,不需要自动刷新、分布式锁和完整框架生态,可以抽一个轻量 starter,本文的 l2cache-starter-demo 已给出最小闭环。
  3. 如果正在规划 Spring Boot 4,先用 springboot4-l2cache-demo 验证底层模型,再用 boot4-internal-l2cache-demo 这种内部 starter 承接注解、序列化和分布式一致性能力。
  4. Layering Cache 可以作为竞品参考,但 Boot4 下要先跑兼容性探针,尤其关注运行时依赖、Kryo 注册和多节点一致性。
  5. 不管选哪条路,都必须用真实 Redis、真实应用启动和连续请求验证 dbLoads 与命中统计。缓存设计不是 PPT 能证明的,它要在本地和流水线里被反复敲打。

附:五份工程快速入口

工程

端口

启动命令

验证接口

jetcache-direct-demo

18081

java -jar target\jetcache-direct-demo-1.0.0.jar

/api/products/1

l2cache-starter-demo

18082

java -jar l2cache-demo-app\target\l2cache-demo-app-1.0.0.jar

/api/products/1

springboot4-l2cache-demo

18083

java -jar target\springboot4-l2cache-demo-1.0.0.jar

/api/products/1

layering-cache-boot4-probe

18084

java -jar target\layering-cache-boot4-probe-1.0.0.jar

/api/products/1

boot4-internal-l2cache-demo

1808518086

java -jar internal-l2cache-demo-app\target\internal-l2cache-demo-app-1.0.0.jar

/api/products/1/api/products/stats

有需要demo代码的,请留言。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、痛点引入:老李的高并发噩梦
  • 二、二级缓存是什么
  • 三、读路径与写路径
  • 四、主流方案与资料地址
    • JetCache
    • Layering Cache
    • Spring Boot 4
  • 五、选型矩阵
  • 七、本地工程一:JetCache 直接使用
  • 八、本地工程二:轻量 Spring Boot Starter
  • 九、本地工程三:Spring Boot 4 + Java 17
  • 十、本地工程四:Layering Cache + Spring Boot 4 兼容性探针
  • 十一、本地工程五:Boot4 内部二级缓存 starter
  • 十三、缓存风险与排障图
  • 十四、给 AI Coding 的落地提示词
  • 十五、最终建议
  • 附:五份工程快速入口
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档