多级缓存由Caffeine(本地)和Redis(分布式)构成,Oracle仅作为底层数据源不参与缓存层级;因其是持久化数据库,延迟高(毫秒级)、无应用层可编程缓存接口,强行纳入会违背“逐层加速”设计原则。
oracle 不参与多级缓存架构设计,它只是底层数据源;真正构成“多级缓存”的是 caffeine(本地) + redis(分布式),oracle 只负责兜底查询和写回源头。
为什么 Oracle 不能当缓存层用?
Oracle 是关系型数据库,本质是持久化存储,不是缓存组件。它的查询延迟在几十毫秒起步,远高于 Redis(微秒~毫秒级)和 Caffeine(纳秒级)。强行把它塞进“缓存链”只会拖慢整个流程,违背多级缓存“逐层加速、快速拦截”的设计初衷。
常见误解是把“数据库查询结果缓存”等同于“数据库作为缓存”,这是概念混淆。Oracle 的作用始终是:当两级缓存都未命中时,才被访问;查完后,数据应写入 Redis 和 Caffeine,而非写回 Oracle 缓存区(Oracle 没有应用层可编程的缓存写入接口)。
Spring Boot 中 Caffeine + Redis 的真实集成路径
集成重点不在 Oracle,而在如何让 Caffeine 和 Redis 协同工作——即定义统一的缓存抽象、控制读取顺序、保障写回一致性。Oracle 只需通过 JdbcTemplate 或 JPA 正常接入即可。
- 引入核心依赖:
spring-boot-starter-cache、com.github.ben-manes.caffeine:caffeine、spring-boot-starter-data-redis - 禁用默认缓存管理器,自定义
CacheManager实现类,重写getCache(String name)方法,使其优先返回CaffeineCache实例(L1),但内部封装对RedisCache(L2)的 fallback 调用 - 关键逻辑不在配置,而在缓存操作:比如
@Cacheable注解背后的方法,实际执行时要先查Caffeine,未命中再查Redis,再未命中才走 Oracle 查询,并按“Oracle → Redis → Caffeine”顺序写回 - 避免直接使用
RedisTemplate或Caffeine原生 API 做业务缓存操作,否则会绕过 Spring Cache 抽象,导致注解失效、统计丢失、一致性难维护
Caffeine 和 Redis 的过期策略必须错开
如果两者设置相同过期时间(比如都是 10 分钟),就会出现“集体失效”,引发缓存雪崩——大量请求同时穿透到 Oracle。这不是 Oracle 的问题,而是缓存设计缺陷。
实操建议:
-
Caffeine使用expireAfterAccess(5, TimeUnit.MINUTES):强调“热度维持”,适合短时高频访问 -
Redis使用expireAfterWrite(30, TimeUnit.MINUTES):强调“数据保鲜”,覆盖更长业务周期 - Oracle 层不设过期,只承担最终一致性校验角色(例如配合 binlog 或定时任务做脏数据清理)
这种差异不是随意设定,而是利用了 Caffeine 的访问刷新机制和 Redis 的写入锚点机制,天然形成时间梯度,降低集中穿透风险。
Oracle 数据变更时,如何触发两级缓存失效?
不能依赖 Oracle 自身通知——它没有类似 Redis 的 PUB/SUB 或 Kafka 的事件总线。所有缓存失效动作必须由应用层主动发起。
典型方案是:在更新 Oracle 的 Service 方法里,显式调用 cacheManager.getCache("user").evict(key),该调用会同步清除 Caffeine 和 Redis 中对应 key(前提是你的自定义 CacheManager 实现了双驱逐逻辑)。
容易踩的坑:
- 只清
Redis忘了清Caffeine:导致本机缓存脏读 - 用异步方式清缓存(如
@Async):清缓存失败无感知,且可能因事务未提交导致清早了 - 用
delete而非evict:某些CacheManager实现对delete支持不完整,尤其跨存储类型时
最稳妥的做法是:在同一个事务边界内,完成 Oracle 更新后,立即同步调用两级缓存的 evict,并捕获异常做降级日志记录。
多级缓存真正的复杂点不在怎么连 Oracle,而在于“谁来决定哪一层该读、哪一层该写、哪一层先失效”。这些逻辑一旦散落在各处,就变成不可维护的技术债。所以不要试图让 Oracle 参与缓存决策,它只管存好数据;把控制权牢牢收在应用层的缓存抽象里。


















