★ 多级缓存架构怎么搭?本地缓存与 Redis 各自扮演什么角色?

结论先行:典型三级是 L1 进程内缓存(Caffeine/Guava)→ L2 Redis → L3 MySQL。本地缓存命中是微秒级且零网络开销,承担最热数据的挡板;Redis 承载大规模共享缓存;数据库兜底。核心难点不在读,而在写后如何让各级缓存及时、不过度地失效。

一、为什么分三级

层级延迟量级容量一致性代价定位
L1 Caffeine微秒级小(几百 MB)各实例不一致,需广播失效挡住大部分热点读
L2 Redis毫秒级全局基本一致共享回源兜底
L3 MySQL毫秒~十毫秒无限强一致最终数据源
读:L1 未命中 → L2 未命中 → L3 查询 → 回填 L2、L1
写:先更新 DB → 删/更 Redis → 广播清 L1(见第 40 章)

二、L1 的选型与配置

Caffeine.newBuilder()
    .maximumSize(10_000)                                // 容量上限
    .expireAfterWrite(30, TimeUnit.SECONDS)             // 过期兜底
    .recordStats()                                      // 统计命中率
    .build(key -> loadFromRedis(key));                  // 未命中回源

三、各级缓存职责分工

  • L1 只放大颗粒热点数据(商品详情、配置、用户基础信息),容量要克制,防止 GC 压力。
  • L2 是所有实例共享的权威缓存,承担穿透保护:空值缓存、布隆过滤器挡不存在的 key。
  • 回填防击穿:L2 回源加互斥锁(分布式锁),只让一个请求查库。

四、一致性与失效

  • 变更路径:更新 DB → 删除/更新 Redis → 通过 Redis Pub/Sub 或 MQ 广播删除各节点 L1。
  • L1 删除失败兜底:短 TTL(30~60 秒),让不一致窗口可控。
  • 一致性是性能换来的:接受秒级窗口,不追求缓存强一致。

常见追问与记忆点

  • 追问:本地缓存会导致缓存漂移吗?会,节点间 L1 不同步,所以 L1 只放可容忍短暂不一致的数据并配短 TTL。
  • 追问:为什么要 Caffeine 而不是自己写 Map?容量/过期/统计/并发控制都要自造,Caffeine 内置 W-TinyLFU 淘汰与异步加载。
  • 记忆点:L1 挡热点(微秒)、L2 保共享(毫秒)、DB 兜底;读防击穿穿透,写靠删除 + 广播 + 短 TTL 收敛。
笔记加载中…