news 2026/9/6 10:08:55

Spring Cache 多缓存后端怎么选:Caffeine、Redis 与 NoOp 的路由边界

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Cache 多缓存后端怎么选:Caffeine、Redis 与 NoOp 的路由边界

Spring Cache 多缓存后端怎么选:Caffeine、Redis 与 NoOp 的路由边界

Spring Cache 同时配置 Caffeine、Redis 时,首先要回答的不是“有几级缓存”,而是一个缓存名称最终由哪个Cache实现承接。这个路由规则如果不清楚,开发者很容易误判读取顺序、故障降级和清理语义。

很多项目看到L2CacheManager、Caffeine 和 Redis 同时存在,就默认系统会先查本地缓存、未命中再查 Redis。MetaLite 当前源码并不是这种读穿链路:它按缓存名称选择一个后端,找不到配置时再降级到NoOpCache

本文聚焦 Spring Cache 的多后端路由契约,逐项验证 Caffeine、Redis 与 NoOp 的选择顺序、同步加载和清理边界。至于 Caffeine 本地副本怎样跨实例失效,已经由文章 051 独立讲解,这里不再重复展开。

一、L2CacheManager 实际返回哪一个 Cache

MetaLite 将L2CacheManager注册为主CacheManager。它收到缓存名称后按顺序判断:

if(caffeineCacheManager!=null){Cachecache=caffeineCacheManager.getCache(name);if(cache!=null)returncache;}if(redisCacheManager!=null){Cachecache=redisCacheManager.getCache(name);if(cache!=null)returncache;}returnnewNoOpCache(name);

这段逻辑的关键是:返回 Caffeine 以后,请求不会继续访问 Redis。

所以当前L2CacheManager更准确的定位是“缓存后端选择器”:某个名称优先使用 Caffeine;Caffeine 没有该名称时才尝试 Redis;两边都没有则返回 NoOpCache。

它不是一个把 L1 和 L2 串起来的读穿 Cache。

二、按名称选择有什么价值

这种模式仍然有实际价值。团队可以把缓存按数据性质分开:

  • 极高频、允许短暂不一致的数据放 Caffeine;
  • 多实例必须共享的数据放 Redis;
  • 不需要缓存的环境关闭组件,让注解自动退化。

业务代码继续使用 Spring Cache 注解,不需要到处判断当前环境启用了哪种缓存。

但配置同名实例时,Caffeine 会优先,Redis 中即使存在同名配置也不会参与读取。这个优先级必须写进配置规范,否则维护者容易误以为两层都生效。

三、NoOpCache 为什么比返回 null 更安全

当两个缓存都未启用,L2CacheManager为缓存名称创建NoOpCache。它不保存数据,读取永远未命中,但能让 Spring Cache 调用链继续执行真实方法。

这种降级避免了开发、测试或轻量服务因为没有 Redis 而启动失败。

边界同样明确:缓存被误关闭时,系统不会立即报错,而是把全部流量打到真实方法或下游服务。可选能力提高了可用性,也要求监控缓存命中率和下游负载。

四、Caffeine 的 sync=true 解决什么问题

CaffeineCache.get(key, valueLoader)使用 Caffeine 原子加载,同一 JVM 中同一 key 可以只让一个线程执行加载逻辑。这正是@Cacheable(sync = true)想解决的缓存击穿问题。

但它只约束当前进程。十个服务实例同时未命中时,仍可能各有一个请求访问数据库或 RPC。

跨实例单飞需要分布式锁、请求合并或其他协调机制,不能由本地sync=true自动得到。

五、RedisCache 的 synchronized 不是分布式锁

RedisCache.get(key, valueLoader)使用实例方法上的synchronized

publicsynchronized<T>Tget(Objectkey,Callable<T>valueLoader)

它只能串行化当前 JVM、当前 Cache 对象上的加载,而且锁粒度覆盖所有 key。多个应用实例之间仍然会同时加载,单实例内不同 key 也可能互相等待。

如果目标是分布式防击穿,这个关键字不够;如果目标是细粒度本地合并,它的锁粒度又偏大。

六、Redis clear 当前并没有实现

RedisCache.clear()目前是空方法。调用@CacheEvict(allEntries = true)时,不能据此假设 Redis 中该缓存的所有键已经清除。

这也是为什么缓存抽象必须逐个验证方法语义:实现了 SpringCache接口,不代表每个操作都具有完整的分布式语义。

批量清理还涉及 key 前缀、SCAN、阻塞风险和误删边界,需要独立设计。

七、为什么“按名称路由”不能叫二级缓存

文章 051 处理的是多个 Caffeine 本地副本如何同步删除 key;055 讨论的是一个缓存名称最终选择哪个后端。前者属于失效通知,后者属于 CacheManager 路由,两者不能合并成同一个“缓存一致性”主题。

两者经常被混在一起:

  • 跨实例失效属于一致性通知;
  • L1→L2 读穿属于多层读取策略;
  • L2CacheManager当前实现的是后端选择。

概念分清以后,才能准确判断系统已经保证了什么。

八、如果要做真正的两级读穿需要什么

一个完整的组合 Cache 至少要定义:

  1. L1 未命中是否读取 L2;
  2. L2 命中后是否回填 L1;
  3. put、evict、clear 以什么顺序写两层;
  4. L2 故障时放行、失败还是只用 L1;
  5. 空值、TTL 和序列化规则怎样对齐;
  6. 并发加载如何避免多实例击穿。

这些语义没有实现之前,名称里出现 “L2” 也不应被宣传成完整的二级缓存链。

九、缓存设计首先要把语义说准确

MetaLite 当前实现提供了三种可用路径:Caffeine、Redis 和 NoOp,并通过统一CacheManager接入 Spring 注解体系。这已经减少了业务侧配置分叉。

下一步无论继续保持“按名称选后端”,还是演进为真正的两级缓存,都应该让命名、文档、监控和代码行为一致。缓存最危险的问题往往不是没有命中,而是团队以为系统具有一种实际上并不存在的保证。

十、四个断言可以证明它不是自动二级缓存

不要只看类名L2CacheManager。可以用四个断言验证真实语义:

  1. 同一个缓存名最终只返回一个Cache实例,而不是先查 Caffeine、未命中再查 Redis;
  2. 选择 Redis 后,本地 Caffeine 不会自动保存同一份值;
  3. RedisCache.clear()当前没有提供完整全量清除语义;
  4. 本地缓存跨实例失效解决的是 Caffeine 一致性,不会自动组成读穿式 L1/L2。

这四个验证结果决定了配置和故障预期。把“可以按名称选择本地或远程缓存”写成“已经具备二级缓存”,会让开发者错误推断命中顺序、回填和失效行为。


框架简介
MetaLite 是面向企业生产环境的新一代 Java 微服务技术底座。系列文章重点分享代码背后的设计思路、技术取舍与工程实践。

源码基线
JDK 21、Spring Boot 3.2.9、Spring Cloud 2023.0.1、Spring Cloud Alibaba 2023.0.1.3,具体组件版本以项目backend-bom为准。

作者简介
15 年 Spring 体系企业级开发经验,专注于 Java 微服务架构、工程治理与生产实践。

持续更新
MetaLite 系列内容将持续更新,围绕核心设计、源码链路、技术取舍与生产实践展开。欢迎关注作者,及时获取后续内容。

在线演示
演示地址: https://admin.metalite.top/
演示账号: guess
演示密码: admin@2026

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 8:36:03

Feign 超时时间明明设了 3 秒,为什么实际等了 10 秒才返回超时?

这个问题我相信不少人遇到过。配置文件里写得清清楚楚&#xff1a;feign:client:config:default:connectTimeout: 3000readTimeout: 3000下游服务一旦变慢或者挂掉&#xff0c;按预期最多 3 秒就应该超时返回。但实际一看监控&#xff0c;超时时间稳定在 10 秒左右。3 秒变 10 …

作者头像 李华
网站建设 2026/8/31 12:47:57

C++函数模板实现通用排序算法:从快速排序到自定义比较器

1. 项目概述&#xff1a;为什么我们需要排序函数模板&#xff1f;在C开发中&#xff0c;数据排序是一个高频到不能再高频的操作。无论是处理用户列表、分析日志时间戳&#xff0c;还是优化游戏中的物体渲染顺序&#xff0c;你几乎每天都在和排序打交道。新手可能会为每一种数据…

作者头像 李华
网站建设 2026/9/2 8:13:02

富国基金大数据面试题解析:数仓建模、实时计算与数据质量实战

先声明一下&#xff0c;这篇不是官方题解&#xff0c;也不是什么标准答案大全。我是结合这两年富国基金大数据岗的公开面经、候选人的复盘反馈&#xff0c;再叠加我自己在金融数据领域多年的落地经验&#xff0c;把题目背后的考察逻辑、技术原理和实操细节重新梳理了一遍。整理…

作者头像 李华
网站建设 2026/8/31 18:46:22

基于WebGIS的校园新生导航系统:从技术选型到实战部署全解析

简介&#xff1a;本资源是一个基于WebGIS技术构建的校园新生导航系统完整实现方案&#xff0c;面向高校GIS开发初学者、Web前端学习者及地理信息专业学生&#xff0c;旨在解决新生入校后对教学楼、宿舍、食堂等场所定位难、路线不熟的实际问题。系统融合地图展示、实时定位、路…

作者头像 李华
网站建设 2026/8/31 2:06:16

Agent Skills:从提示词到可复用技能,构建AI协作新范式

很多人对 Agent Skills 的第一反应是&#xff1a;这不就是给 AI 写一套更长的提示词吗&#xff1f;我一开始也这么想。直到我在一个项目里连续几天重复粘贴同样的背景说明、格式要求、输出样例&#xff0c;才意识到真正的问题不是模型不够聪明&#xff0c;而是我的工作方式一直…

作者头像 李华
网站建设 2026/9/1 9:52:27

通用大模型写论文真够用?专业平台能补哪些环节

通用大模型答得挺顺&#xff0c;可论文要的文献和检测它能给吗&#xff1f;这是很多同学开题前的真实纠结。坦诚说&#xff0c;通用大模型&#xff08;如 ChatGPT、DeepSeek&#xff09;在开放式问答和思路梳理上确实顺手&#xff0c;但论文写作里有两道硬关卡——真实可查的参…

作者头像 李华