Cache Patterns and Pitfalls

Cache-aside, read/write-through, write-behind, cache penetration/breakdown/avalanche, consistency strategies.

IntermediateUpdated 2026-10-2974K views

Three Read/Write Patterns

模式读路径写路径特点
旁路缓存 Cache-Asidemiss 后回查 DB 并回写先更 DB 再删缓存最常用,需处理不一致
读穿透 Read-Through缓存层同步回源缓存层写入并同步到 DB逻辑集中在缓存层
异步写 Write-Behind同 Read-Through只更缓存,异步刷 DB写最快,容忍丢数据

Write deletes the cache instead of updating it: concurrent updates make an overwritten value stick, and most caches lack fine-grained write APIs. Deletion is idempotent and simpler to reason about.

The Classic Inconsistency Window

线程 A 更新 DB → 删缓存
线程 B 读:缓存 miss → 查 DB → 回写旧值

兜底:
  1) 缓存过期(防爆 + 最终一致)
  2) 延迟双删:删缓存 → 更新 DB → 延迟 1s 再删一次
  3) 订阅 binlog 异步删缓存(Canal/Debezium)——最干净

问答:先删缓存再更新 DB?
  更差:删完到 DB 更新之间,别的读把旧值又写回缓存

强一致场景别硬拆缓存:直接查库或用「版本号 + 读时校验」

Penetration, Breakdown, Avalanche

问题现象解法
穿透查不存在的 key,每次打到 DB空值缓存(短 TTL)/ 布隆过滤器
击穿热点 key 过期瞬间高并发回源互斥锁重建 / 永不过期 + 异步更新
雪崩大量 key 同一时刻过期TTL 加随机抖动 / 多级缓存 / 限流降级
互斥重建(GO 伪码):
  v = GET(k)
  if v == nil:
      if TRYLOCK(k):           # SET nx px 拿到重建权
          v = DB(k); SET(k, v, ttl + rand); UNLOCK
      else:
          sleep(50ms); 重试      # 没抢到的稍等再读

布隆过滤器(Redisson Bloom / 本地位图):
  写入时加位;查询前先判存在
  可删的用「布谷鸟过滤器」,普通布隆不能删元素

Multi-Level and Local Caches

  • JVM/进程内本地缓存(Caffeine)挡 9 成流量,Redis 兜第二级,DB 最后。
  • Local caches get stale: size them small, set short TTLs, and listen to invalidation events (Redis pub/sub or MQ).
  • Size with Caffeine: maximumSize + expireAfterWrite + recordStats for hit rate.
  • Remember the effective metric is DB pressure, not cache hit rate alone.

Friends & Partners