跳转至

缓存:读路径的副本治理与泄压阀

不要把缓存简单理解为“变快”,它在架构中更本质的角色是读路径的副本层数据库的泄压阀

1. 读路径的工程语义

当一个热点请求(如“热门制度详情”)进入系统时,查库本身没错,错在高频重复的无意义回源

  • Key 语义即真相定义:Key 不是字符串,是副本的索引。一个合格的 Key 必须包含 业务前缀:租户ID:对象ID:版本号(e.g., policy:t42:doc108:v7)。漏掉租户会酿成串数据事故,漏掉版本会命中过期副本。
  • 空值缓存(Anti-Entropy):对于不存在的 ID,必须缓存一个空值副本(带短 TTL),防止非法请求直穿数据库。
  • Singleflight(并发坍缩):在热点 Key 失效瞬间,不应允许 100 个 goroutine 同时扑向数据库。第一个请求去回源,其余 99 个在闸门处等待结果,这就是并发回源合并

2. Redis:内存执行模型与故障语义

Redis 之所以成为标准选型,是因为其单线程原子性毫秒级内存响应

  • 执行模型:Redis 的性能瓶颈通常不在网络,而在大 Key慢命令(如 KEYS * 或大 Hash 全量读取)。这些操作会阻塞事件循环,导致 P99 飙升。
  • 持久化权衡
    • RDB (Snapshot):恢复快,但丢数据多。适合对一致性要求不高的副本。
    • AOF (Append Only):数据更安全,但 IO 压力大。适合承载业务状态。
  • 复制与拓扑:主从保证可用性,Sentinel 负责故障自动切换,Cluster 解决横向扩展。关键直觉:分片不均会导致“热点分片”,单点容量上限仍是系统天花板。

3. 缓存模式:Cache-Aside 是默认选择

不要过度设计,先看 Cache-Aside 的受力点。

模式 逻辑 适用场景 架构代价
Cache-Aside 读时回填,写后失效 90% 业务场景 需自行处理并发 miss 与回填逻辑
Write-Through 同步写库与缓存 强一致读需求 写链路变长,延迟增加
Write-Behind 先写缓存,异步落库 高吞吐/日志流 一致性窗口大,存在丢数据风险

4. 故障演进:从击穿到雪崩

这三个词不是绕口令,而是读路径失守的三个阶梯:

  1. 击穿 (Breakdown):单个热点 Key 过期,瞬间流量直击 DB。解药:Singleflight / 互斥锁。
  2. 穿透 (Penetration):大量不存在的 Key 请求直达 DB。解药:布隆过滤器 / 空值缓存。
  3. 雪崩 (Avalanche):大批 Key 集中过期或 Redis 宕机,全量请求压垮 DB。解药:TTL 随机抖动(Jitter)/ 熔断降级。

5. 排查顺序:第一反应

当系统变慢且疑似缓存问题时: 1. 观测命中率:是否出现断崖式下跌? 2. 检查 Redis 监控:是否存在 CPU 飙升(慢命令)或内存打满(驱逐策略触发)? 3. 分析回源压力:DB 的 WaitCountActiveConn 是否因为缓存失效而暴涨? 4. 校验 Key 语义:是否存在 Key 冲突、租户遗漏或序列化版本不一致?

结论: 好的缓存设计不是为了“快”,而是为了让系统在面对热点流量时,依然拥有可预测的行为确定的回源压力