题记与说明
综合说明:本报告由课题两份章节综合而成——纵轴章(发展脉络 1970–2026)与横轴章(2026-11-05 时间截面横向对比)。两章主要事实均在撰写当日经 WebSearch / 网页读取检索验证并随文附来源 URL(个别背景性陈述未附链接,folklore 类内容随文明确标注);本稿为按独立审读意见修订后的终稿,统一了两章间的版本表述(Redis/Valkey 版本号、淘汰策略建议等差异点在正文以括注说明),并合并了两章重复的"个人服务落地清单"(见第三节)。第三方 benchmark 数字未在本机复测,仅作定性参考。撰写日期:2026-11-05。
TL;DR
旁路缓存(cache-aside)自 2003 年 memcached 定型至今从未被替代,演化的是围绕它的失效防御体系。击穿防御链清晰:互斥锁(nginx 2011)→ 租约(Facebook 2013)→ 单飞(2013/2016)→ 概率提前过期(XFetch 2015 → Cloudflare 2024 落地)→ 库内建默认(HybridCache)。雪崩/穿透/击穿三分法是中文社区 2017 年定型的本土词典,英文统称 cache stampede。引擎层正被许可证大战重排(Redis 8 vs Valkey),但缓存模式横跨所有引擎原样成立——模式比引擎稳定一个数量级。个人服务用三件套(TTL 抖动、进程内单飞、空值缓存+参数校验)即可覆盖三类事故的主要攻击面,分布式锁与布隆过滤器默认不上。
三个核心词,先记住再说(详细展开见第二节"先厘清三个词"):
- 雪崩:大量 key 同时过期(或缓存层整体宕机),请求洪峰直接砸向数据库;
- 击穿:单个热 key 过期瞬间,海量并发同时回源(= 只打一个洞的雪崩);
- 穿透:查询根本不存在的数据,缓存永远不命中,每次都穿透到库(常为恶意扫描)。
一、纵轴:发展脉络(1970–2026)
一条主线
缓存工程二十多年的演化可以概括为一句话:它从"给数据库打的临时补丁"(2003 年的 memcached),长成了拥有自己的故障分类学(雪崩/穿透/击穿)、防御工具箱(租约/单飞/概率提前过期/逻辑过期)和治理体系(TTL 治理、热点治理、许可证治理)的一门工程学科。而驱动每一次跃迁的从来不是缓存本身,而是"回源代价"的变化——回源到数据库、回源到第三方 API,如今还包括回源到按 token 计费的 LLM。
阶段一:问题域的奠定(1970–2002)
- 1970 年,Burton H. Bloom 发表布隆过滤器论文,为半个世纪后的"防缓存穿透"备好了最经典的武器(Wikipedia: Bloom filter)。
- "惊群"(thundering herd)概念源自操作系统调度:多个进程阻塞在同一事件上,事件触发时全部被唤醒(Wikipedia: Thundering herd problem)——这是后来"缓存击穿/雪崩"叙事的母概念。
- 1990 年代中期代理缓存兴起:Squid 源自 1994 年的 Harvest 项目(Wikipedia: Squid));1999-06 发布的 RFC 2616 定义了 HTTP 缓存语义,2014 年由 RFC 7234 整理为独立规范(RFC 2616、RFC 7234)。
- "Computer Science 只有两件难事:缓存失效与命名"常归于 Netscape 的 Phil Karlton(folklore,无可考原始文献)。
驱动因素:Web 流量爆炸使"重复计算/重复回源"第一次成为产业级问题,缓存失效之"难"在这一时期成为共识。
阶段二:旁路缓存定型——memcached 与第一代分布式缓存(2003–2008)
- 2003-05-22:Brad Fitzpatrick 在 LiveJournal(Danga Interactive)发布 memcached,动机是扛住博客站访问量——据 Ars Technica 十周年回顾,他是在等待新数据库服务器到货期间写出来的。最初用 Perl 实现,后由 Anatoly Vorobey 重写为 C,即今天所有人用的版本(Wikipedia: Memcached、Ars Technica: Gimme the cache!、memcached.org/about)。
- "旁路缓存"(cache-aside / look-aside)模式随 memcached 定型:应用自己读缓存,未命中回源数据库再回填,缓存不参与写路径;写路径是"更新数据库后删除缓存"。该模式后来被 Microsoft Azure 云设计模式正式命名为 Cache-Aside Pattern(Microsoft Learn)。
- 同期 Java 生态出现 ehcache、OSCache、JBoss Cache 等进程内/早期分布式缓存库。
- "缓存过期瞬间请求全部压向数据库"的现象在英文社区被称为 dogpile effect(堆积效应),是"击穿"问题的早期口语称呼(Scalable Thread: Dogpile problem)。
主要参与者:Brad Fitzpatrick、Anatoly Vorobey、dormando、Dustin Sallings;Digg、Slashdot、Wikipedia 等早期 LAMP 大站。驱动因素:Web 2.0 流量激增 + 单机数据库瓶颈——"内存哈希表 + 文本协议"的极简设计把缓存从进程内技巧变成了可水平扩展的网络基础设施。
阶段三:大规模生产验证与机制化(2009–2013)
- 2009-03:antirez(Salvatore Sanfilippo)为实时分析服务 LLOOGG 寻找比 MySQL 更快的方案,从 Tcl 原型重写为 C,Redis 首次公开发布(仓库保留 2009-03-22 的代码,项目实际始于 2008 年)(Wikipedia: Redis、antirez/historical-redis-versions)。2020 年 6/7 月 antirez 退出维护,发表《The end of the Redis adventure》(antirez.com/news/133)。
- 2010-05:Mark Nottingham 发布 RFC 5861,定义
stale-while-revalidate与stale-if-error——"先返回旧值、后台刷新"自此有了标准词汇(RFC Editor)。 - 2011-12:nginx 1.1.12 引入
proxy_cache_lock:并发 MISS 只放一个请求回源,其余等待——反向代理层的"请求合并/防击穿"标准化(nginx 文档、Simon Willison 2017)。 - 2011-12-13:Spring 3.1 GA,带来缓存抽象
@Cacheable/@CachePut/@CacheEvict,企业 Java 把 cache-aside 写进了注解(Spring 3.1 goes GA、Spring 3.1 M1: Cache Abstraction)。 - 2012:Twitter 开源 Twemproxy,解决数千前端应用对缓存服务器的连接风暴与分片问题(Twitter Engineering)。
- 2013-04:Facebook 发表 NSDI'13 论文《Scaling Memcache at Facebook》——旁路缓存的"圣经"(USENIX 论文 PDF):lease(租约)机制在 miss 时下发 64-bit 租约、回填时校验,杜绝并发旧值覆盖新值(stale set);对热点 key 可在短窗口内扣发租约,只允许一个客户端回源——防击穿的工业级解法。regional pools、remote markers、McSqueal 则定义了"删缓存而非改缓存"的跨区失效传播体系,接受最终一致、以数据库为权威源。
- 2013-07:Brad Fitzpatrick(此时已在 Google)发布 groupcache,内嵌 singleflight 包——同 key 并发回源合并为一次调用;后移入
golang.org/x/sync/singleflight,成为 Go 生态标配(groupcache/singleflight、pkg.go.dev 文档)。 - Netflix 于 2013 年引入 EVCache,2016-03 发表《Caching for a Global Netflix》,多区域复制 + 按数据"波动性"分级过期(Netflix Tech Blog)。
驱动因素:Facebook/Twitter/Netflix 级流量把缓存从组件升格为独立架构层;一致性(stale set)、击穿(lease 扣发)、跨区失效传播第一次被论文化。
阶段四:方法论体系化与中文社区词典定型(2014–2018)
- 2015-04:Redis 3.0 GA,Cluster 生产可用(16384 槽位、自动分片与故障转移)——分布式缓存自己长出了高可用(Reddit 官方发布帖)。
- 2015-12:TinyLFU 论文上线(arXiv:1512.00727),Ben Manes 据此实现 Caffeine(W-TinyLFU:窗口 + 频率草图),2016-01 经 High Scalability《Design of a Modern Cache》推向主流,后成为 Spring Boot 默认本地缓存(arXiv、High Scalability、Caffeine)。
- 2015:XFetch——Vattani、Chierichetti、Lowenstein《Optimal Probabilistic Cache Stampede Prevention》(PVLDB 8(8), 2015),源自 Netflix 内部"波动性过期"实践:当距上次重建的时间超过
β·Δ·ln(1/rand(0,1))(rand∈(0,1),Δ 为回源耗时期望 E[recompute])时提前刷新,"尚未提前刷新"的概率随时间按 exp(-t/(β·Δ)) 指数衰减,无需锁即可把过期曲线拉平——"击穿"问题第一次获得理论最优解。该判定式与第二节平面 C-2 表中"软过期时刻 = time + β·ln(1/rand)·Δ"是同一算法的两种等价写法(论文 PDF、Wikipedia: Cache stampede 伪代码)。 - 2016-06:Spring 4.3 GA,
@Cacheable(sync=true)——框架层直接内建"同 key 并发加载只执行一次",击穿防御进了注解(Spring 4.3 新特性)。 - 2016-02:Martin Kleppmann《How to do distributed locking》质疑 RedLock,antirez 随即回应《Is Redlock safe?》——用分布式锁做"互斥重建"的安全边界被严肃检验(Kleppmann、antirez)。
- 2017-03-17:美团技术团队明辉发表《缓存那些事》(《程序员》2017 年第 3 期),把缓存穿透 / 缓存击穿 / 缓存雪崩三分法连同布隆过滤器、缓存空对象、互斥锁、过期时间加随机值、多级缓存等对策一次定型——这是中文社区该词典最重要的定型文献(tech.meituan.com)。需说明:这三个词是中文技术圈的本土化造词(英文社区一般只用 cache stampede / thundering herd),没有可考的单一"首提出"文献,经阿里系面试文化与国内博客传播普及;后续《凤凰架构》(周志明)与小林 coding 等资料进一步固化三者区分(凤凰架构、小林 coding)。
- 2017 年起:《阿里巴巴 Java 开发手册》(2017 首发,2019 华山版、2020 泰山/嵩山版)把高并发下的缓存规约纳入大厂工程规范体系(阿里云开发者社区)。
- 2017-07:Redis 4.0 发布,新增 LFU 淘汰策略——热点治理进入缓存内核(Redis 官方博客)。
- 2017 年前后:阿里开源 canal,用 MySQL binlog 订阅把"写库后失效缓存"做成基础设施(github.com/alibaba/canal);"先更新库再删缓存 vs 先删缓存再更新库""延迟双删"等一致性招式在此期间被社区反复淬炼(folklore 性质,无单一文献)。
驱动因素:国内移动互联网大促(双 11/618)的极端脉冲流量叠加面试文化,把问题域"词典化";学术界(XFetch、TinyLFU)与工业界(lease、singleflight)在击穿问题上正式会师。
阶段五:精细化治理——热点、多线程与体验优化(2019–2023)
- 2019:Next.js/React 生态把 stale-while-revalidate 思想前端化为 "SWR" 数据获取模式,RFC 5861 的词汇出圈到前端社区(Vercel SWR、vercel/swr)。
- 2020-04-30:Redis 6.0 发布多线程 I/O(命令执行仍单线程,网络读写并行化,吞吐最高翻倍)——单线程模型在缓存并发极限上让步(Redis 官方博客)。
- 2020 前后:京东开源 JD-hotkey 热点探测框架:client 实时上报 key → worker 滑动窗口统计 → 毫秒级把热 key 推送到应用 JVM 本地缓存,经 618/双 11 验证(京东云设计与实践);得物等给出基于 Redis 内核的替代方案(得物技术)——"热 key 治理"成为独立课题。
- Netflix 2018 年发表《Evolution of Application Data Caching: From RAM to SSD》(Netflix TechBlog),缓存分层向闪存下沉;memcached 于 2020 年前后发布大重构的 1.6 系列并引入可选 proxy 能力,这位"祖师爷"转入稳态维护。
- 2022-01:Cloudflare 承认其 stale-while-revalidate 支持并非真正异步(Cloudflare Community);直到 2026-02-26 才在 changelog 宣布完全异步 SWR——标准采纳滞后于论文 16 年(Cloudflare changelog)。
- 2023-04-11:Zilliz 发布 GPTCache,LLM 语义缓存登场:以向量相似度判定命中,缓存对象从"数据库行"变成"模型回答",命中判定从"键相等"变成"语义相近"(Zilliz 博客、ACL NLP-OSS 2023 论文、GitHub)。
驱动因素:热点经济(明星、秒杀、爬虫)、多云架构、前端体验指标(Core Web Vitals),以及 LLM 的 token 成本——"回源代价"被推到新维度。
阶段六:分叉、回归与当下(2024–2026)
- 2024-03-20:Redis Ltd. 宣布自 7.4 起改用 SSPLv1/RSALv2 双许可,矛头指向云厂商,社区哗然(Redis 官方公告)。
- 2024-03-28:仅 8 天后,Linux Foundation 宣布 Valkey,从最后的 BSD 版本 Redis 7.2.4 分叉,AWS、Google Cloud、Oracle、Ericsson 联合背书(Linux Foundation 新闻稿、A Year of Valkey)——缓存基础设施的"供应链政治"第一次如此戏剧化。
- 2025-05-01:Redis 8.0 GA,加入 AGPLv3 三许可(AGPLv3/RSALv2/SSPLv1),重新成为 OSI 意义上的开源软件,与 Valkey 形成双轨竞速(Redis 官方博客、LWN、InfoQ)。
- 2024-12-26:Cloudflare 发表《Sometimes I cache: implementing lock-free probabilistic caching》,把 XFetch 论文落到 Workers 生产代码:以指数分布"掷骰子"决定是否提前回源,无锁、无外部依赖地压平过期峰值(blog.cloudflare.com)——击穿防御进入"去中心化概率"阶段。
- 2026:Valkey 持续演进(详见横轴引擎对比),Redis 与 Valkey 的竞争回到工程本身(内存效率、复制协议);AI 网关与 RAG 服务把语义缓存、prompt 缓存变成新的旁路缓存场景。
驱动因素:云厂商与独立软件公司利益失衡 → 许可证武器化 → 社区分叉;主战场从"防数据库崩溃"扩展到"防 LLM 账单爆炸"。
纵轴各阶段一览表
| 阶段 | 关键版本/事件 | 主要参与者 | 驱动因素 |
|---|---|---|---|
| 1970–2002 | 布隆过滤器、thundering herd、RFC 2616 | OS/学术/HTTP 社区 | Web 流量爆炸 |
| 2003–2008 | memcached(2003)、dogpile effect | Brad Fitzpatrick 等 | Web 2.0、单机 DB 瓶颈 |
| 2009–2013 | Redis(2009)、RFC 5861、nginx cache lock、Spring 3.1、NSDI'13 lease、singleflight | antirez、Facebook、mnot、Fitzpatrick | 超大流量公司的架构层化 |
| 2014–2018 | Caffeine/TinyLFU、XFetch、Spring sync、美团《缓存那些事》、Redis Cluster/LFU | Netflix 系、Ben Manes、美团/阿里 | 大促脉冲流量 + 面试文化词典化 |
| 2019–2023 | JD-hotkey、Redis 6 多线程、SWR、GPTCache | 京东、antirez、Zilliz | 热点经济、前端体验、LLM 成本 |
| 2024–2026 | 许可证变更、Valkey 分叉、Redis 8 AGPL、无锁概率缓存 | Linux Foundation、Redis Ltd.、Cloudflare | 供应链政治、AI 时代回源代价 |
二、横轴:横向对比(2026-11-05 时间截面)
对比对象的选取与维度
"缓存工程实践"是模式与工程层的课题(怎么用缓存、怎么防失效事故),但工程实践从不孤立于引擎存在——你选的策略必须落在某个缓存实现上。因此在当下时间截面上,对比对象拆成三个平面:
- 平面 A:分布式缓存服务器(引擎层)——Redis 8、Valkey、Dragonfly、KeyDB、Memcached、Garnet;
- 平面 B:进程内本地缓存库(库层)——Caffeine、Guava Cache、Ristretto/Otter(Go)、.NET HybridCache、自制 map+TTL;
- 平面 C:缓存读写模式与失效/防御策略(模式层)——cache-aside / read-through / write-through / write-behind,以及 TTL 抖动、singleflight、逻辑过期、概率提前过期(XFetch)、空值缓存、布隆过滤器等防御手段。
对比维度:能力、性能与资源成本、经济成本(许可证/托管)、生态、成熟度与维护状态、适用场景。
先厘清三个词:雪崩 / 穿透 / 击穿
这三个词经常被混用,但防御手段完全不同(核心定义已前置到 TL;DR 之后,此处展开并补充英文对应与出处):
- 雪崩:大量 key 同时过期(或缓存层整体宕机),请求洪峰直接砸向数据库;
- 击穿:单个热 key 过期瞬间,海量并发同时回源(= 只打一个洞的雪崩);
- 穿透:查询根本不存在的数据,缓存永远不命中,每次都穿透到库(常为恶意扫描)。
英文社区对前两者一般统称 cache stampede / thundering herd(Wikipedia: Cache stampede),不细分这三层;三分法是中文社区对同一问题域的本土切分(见纵轴阶段四,2017 年《缓存那些事》定型)。
平面 A:分布式缓存服务器横向对比
| 维度 | Redis 8 | Valkey 9 | Dragonfly | KeyDB | Memcached 1.6 | Garnet |
|---|---|---|---|---|---|---|
| 治理/出身 | Redis Ltd.,参考实现 | Linux Foundation 项目(2024-03 自 Redis 分叉,AWS/Google 等支持) | DragonflyDB 公司 | 2022-05 被 Snap 收购,此后开发节奏放缓 | 由长期维护者 dormando 个人维护 | Microsoft Research |
| 许可证 | 三选一:AGPLv3 / RSALv2 / SSPLv1(2025-05 起) | BSD-3(完全开源) | 源码可用(第三方对比归为 BSL 1.1,未核读 LICENSE 原文) | BSD | BSD | MIT |
| 线程模型 | 命令执行单线程(I/O 可多线程) | 同 Redis 血统,单线程为主 | 每核一线程、shared-nothing + fiber | 多线程(EF RR 轮询) | 多线程 | .NET 8,多线程,Tsavorite 日志式存储 |
| 数据结构/能力 | 最全:String/Hash/List/Set/ZSet/Stream + 8 内置结构(JSON、TimeSeries、Bloom/Cuckoo/CountMin/TopK/TDigest、Vector Set beta)+ 查询引擎 | 与 Redis 高度兼容;9.0 新增 hash 字段级过期、原子 slot 迁移、集群多 DB | 兼容大部分 Redis API(RESP),模块生态最弱 | Redis 兼容,功能面略滞后 | 仅扁平 KV + slab 管理,无持久化保障(extstore 提供磁盘分层) | 支持 String/ZSet/Bitmap/HyperLogLog 等常用面,无完整集群 |
| 持久化/高可用 | RDB+AOF、主从、Cluster | 同 Redis 血统 | 快照;集群能力在演进 | 支持 | 无内建 HA | 检查点;单节点为主 |
| 性能(第三方基准,定性) | 基线;Redis 8 官方称 30+ 项改进、最高快 87% | 与 Redis 接近,8.0/8.1 内存效率与延迟显著改善 | 多数第三方测试中最快、内存效率最好(官方称 30–60% 更省内存) | 近期社区基准中吞吐偏低 | 中小规模够用,规模上限低 | 多核服务器上单节点吞吐显著优于 Redis(VLDB 论文主张) |
| 生态 | 最强(客户端库、框架适配、托管服务最多) | 快速上升,主流云托管(ElastiCache/Memorystore)已支持 | 增长中,兼容 Redis 客户端 | 缓慢 | 老牌稳定 | .NET 生态友好,其他语言经 RESP 可用 |
| 适用场景 | 默认选项;需要查询引擎/概率结构/向量时 | 想要 OSI 开源 + 云厂商托管对齐者 | 单机大内存、多核、重吞吐热点 | 存量用户维护 | 极简 KV、早期生态绑定 | .NET 栈、多核大内存单机场景 |
逐项要点与来源:
- Redis 8(2025-05-01 GA)是本轮格局变化的枢纽。2024-03 弃 BSD 改双许可直接催生 Valkey 分叉;2025-05 回摆新增 AGPLv3,并宣布 30+ 性能改进、查询引擎与 8 个新数据结构(含布隆过滤器等 5 个概率结构与 beta 版 Vector Set)并入开源核心。来源:Redis AGPLv3 公告、LWN、Redis 8 GA、8.0 What's New、布隆过滤器文档。
- Valkey 是"开源对齐"路线的胜出者:8.0(2024-08)主打内存效率与延迟优化,8.1(2025-04)继续降低延迟与内存占用,9.0(2025-10-21)带来 hash 字段级过期、原子 slot 迁移与集群多 DB。对缓存课题特别相关:hash 字段级过期让"大 hash 内按字段设 TTL"从 hack 变成原生能力。来源:Valkey 内存效率、8.1 GA、9.0、LF 新闻稿。
- Dragonfly 是"多线程重写"路线的代表:每核一线程、shared-nothing、fiber 调度,官方称 30–60% 更优内存效率、单实例可至 TB 级;第三方基准多数显示其吞吐/扩展性领先,但模块生态与 Redis 尚有差距。来源:shared-nothing 文档、官方 features、centminmod 基准、RepoFlow 对比。
- KeyDB 处于存在感下滑通道:同为多线程 Redis 兼容实现,2022 年 5 月被 Snap 收购(TechCrunch 报道),但近期社区基准吞吐垫底、开发节奏偏慢,存量用户为主(RepoFlow、DBPro)。
- Memcached 进入"维护模式":仅 1.6 线活跃,多为修 bug 与仪表化;1.6.25 引入 extstore 磁盘分层与压缩整理。它证明了"扁平 KV + 多线程"十几年后依然够用,但无持久化保障、无内建 HA,功能面不覆盖复杂失效策略需求(LWN: The evolution of Memcached、维护文档、endoflife.date、1.6.25 发布说明)。
- Garnet 是"新硬件新语言"路线:.NET 8 实现、RESP 协议兼容、Tsavorite 日志式存储,多核服务器上单节点吞吐显著优于 Redis(VLDB 论文),MIT 许可;短板是 API 覆盖不全、单节点为主(MSR 项目页、文档、VLDB 论文)。
平面 B:进程内本地缓存横向对比
| 维度 | Caffeine(JVM) | Guava Cache | Ristretto / Otter(Go) | .NET HybridCache | 自制 map+TTL |
|---|---|---|---|---|---|
| 淘汰策略 | W-TinyLFU(准入窗口 + Count-Min 频率草图),命中率接近最优 | 分段 LRU | Ristretto:TinyLFU 准入 + 成本淘汰;新锐 Otter 主张更优 | 依赖底层 L1 实现 | 自定(通常无淘汰或简单 LRU) |
| 并发设计 | 无锁环形缓冲 + 条带化 | 锁竞争明显(多年 backlog 的瓶颈修复) | 高吞吐、带宽友好 | 内建合并锁 | 看实现 |
| 性能(定性) | 官方 JMH 基准大幅领先 Guava,个别淘汰场景达 25× | 基线 | 高吞吐 | 未主打性能,主打正确性 | — |
| 框架地位 | Spring Boot 2.x 起默认本地缓存;Spark 也从 Guava 迁移(SPARK-34309) | 老牌、存量巨大 | Dgraph/Badger 内核出身;社区提醒其维护趋缓 | .NET 9 GA(2025-03),微软官方 | 零依赖 |
| 独有能力 | 频率感知淘汰、刷新前异步 reload | 简单 API | 成本模型(按字节预算) | L1+L2 双层、内建防击穿合并、tag 失效 | — |
| 适用场景 | JVM 服务本地 L1 的默认解 | 遗留代码 | Go 高并发热点 | .NET 服务"一键两层缓存" | 个人小服务/流量极低时 |
逐项要点与来源:
- Caffeine 已完成对 Guava 的世代替代:W-TinyLFU 在 OLTP/LIRS/S3 等负载上取得近最优命中率,读写吞吐显著高于 Guava;Spring Boot 与 Apache Spark 相继选它为默认。来源:Caffeine 基准 wiki、SO 对比、SPARK-34309。
- Go 生态在换代:Ristretto 曾是标准答案,但社区(Otter 作者)指出其维护趋缓、TinyLFU 在部分负载下反而吃亏,并推出 Otter 作为替代——选型以"当前是否活跃维护"为第一判据。来源:Ristretto、Otter: The Evolution of Caching Libraries in Go。
- .NET HybridCache 代表"把防御内建进库"的趋势:统一 L1(进程内)+ L2(如 Redis)API,并发 miss 同 key 时自动合并为一次回源(stampede protection 内建、默认开启),并支持 tag 失效——框架层把 singleflight 语义产品化了。来源:HybridCache 文档、.NET 缓存总览、GA 公告。
- 自制缓存(字典 + TTL):无淘汰质量与命中率可言,但零依赖、零心智负担,对个人小服务常常是"够用"的正确答案。
平面 C-1:缓存读写模式横向对比
| 模式 | 读路径 | 写路径 | 一致性 | 实现方 | 主要风险 | 权威来源 |
|---|---|---|---|---|---|---|
| Cache-Aside(旁路缓存,课题核心) | 应用先查缓存,miss 则查库并回填 | 应用写库,删除缓存 | 最终一致(脏读窗口=TTL) | 应用自己写 | 旧值回填、并发写竞态 | Azure Cache-Aside 模式、AWS ElastiCache 缓存策略 |
| Read-Through | 缓存层自己负责 miss 后加载 | 模式本身不约定;常见做法同 cache-aside(更新库后删缓存) | 类似 cache-aside | 缓存库/中间件 | 需要库支持 loader | AWS ElastiCache |
| Write-Through | — | 同步写缓存+库 | 强(读己之写) | 缓存库 | 写延迟高;AWS 提醒两点:节点故障/重启后缓存被清空、数据需另行重建(常退回 cache-aside 补);写入却从不被读的数据白占缓存、挤出热点 key | AWS 缓存最佳实践 |
| Write-Behind / Write-Back | — | 先写缓存,异步批量写库 | 弱(缓存挂→丢数据) | 缓存库 | 数据丢失窗口 | Hazelcast 模式指南、Redisson 术语表 |
| Refresh-Ahead | 到期前主动刷新热 key | — | 好(热 key 几乎无 miss) | 缓存库/定时任务 | 刷新浪费(冷 key 也刷) | Redis 官方博客:缓存策略 |
要点:Cache-Aside 之所以是默认范式,是因为它把复杂度最小化——应用可控、库无关、写路径简单("更新 DB 后删缓存"而不是"更新缓存")。代价是一致性上限低(TTL 内脏读)与 miss 需要应用自己兜底——这正是雪崩/穿透/击穿三类事故的根源。至于表中列出的"旧值回填、并发写竞态"两个风险,最低限度的对策只有两条:回填一律带 TTL(任何旧值都有兜底过期上限)、只删缓存而不更新缓存(把竞态窗口压缩到"一次回源耗时"量级);个人服务做到这两条即可接受,出现"多实例高频写同一 key"或"读方对秒级旧值敏感"的信号时才需要升级(版本号比对、延迟双删等,见第三节清单第 4 条)。
平面 C-2:雪崩/穿透/击穿防御方案横向对比
| 方案 | 针对问题 | 原理 | 实现成本 | 失效场景/代价 | 来源 |
|---|---|---|---|---|---|
| TTL 随机抖动 | 雪崩 | 过期时间加 ±20% 随机量,错峰 | 极低 | 无实质失效;脏读窗口被拉宽 | AWS 缓存最佳实践 |
| 空值缓存 | 穿透 | 不存在的 key 也缓存(短 TTL,30–60s) | 低 | 内存小耗;攻击 key 完全随机时防线弱;脏窗口 | 小林coding、cnblogs 专题 |
| 布隆过滤器 | 穿透 | 概率成员判断,不存在则直接拦截 | 中(Redis 8 已原生内建) | 误报(可能放过)、不能删(Cuckoo 可删)、需周期重建 | Redis 布隆过滤器文档、阿里云开发者社区 |
| 互斥锁(SETNX/分布式锁) | 击穿 | 同 key 只放一个请求回源,其余等待 | 中(要处理超时/死锁/等待队列) | 等待拖长 P99;锁服务故障反成新风险 | 小林coding |
| singleflight 请求合并 | 击穿 | 进程内同 key 并发合并为一次调用(Go x/sync/singleflight 的 Do/DoChan) | 低 | 仅进程内有效;多实例时每实例各合并一次 | pkg.go.dev/singleflight |
| 逻辑过期(异步重建) | 击穿+雪崩 | 物理不设 TTL,值内存放逻辑过期字段,过期后后台异步刷新,读请求立即返回旧值 | 中高 | 短暂脏读;实现复杂 | OneUptime: Redis Cache Stampede |
| 概率提前过期(XFetch) | 击穿+雪崩 | 按剩余寿命概率性提前重建:软过期时刻 = time + β·ln(1/rand)·Δ(rand∈(0,1) 时提前量恒为正;等价判定式:距上次重建超过 β·Δ·ln(1/rand) 即刷新——与纵轴阶段四所引判定式同式),被证明最优 | 低中(β 需调参) | 需要能预估回源耗时 Δ(= E[recompute]) | Vattani et al. 2015 论文、Wikipedia: Cache stampede 伪代码、Go 实践 |
| 接口参数校验/限流熔断 | 穿透+雪崩 | 非法 ID 直接拒绝;缓存层故障时限流降级读库 | 低 | 拦不住"合法但不存在"的 key | 小林coding |
| 多级缓存(L1+L2) | 雪崩+击穿 | L1 吸收热点与 L2 故障,L2 提供共享 | 中(一致性管理变复杂) | L1 加剧脏读与失效广播复杂度 | HybridCache 文档 |
| 缓存层高可用 | 雪崩 | 主从/集群,避免单点宕机即雪崩 | 架构级 | 成本上升 | Dragonfly: Valkey vs Redis 指南 |
逐项点评:
- 没有银弹,只有组合拳。中文社区的主流实践结论一致:穿透用"参数校验 + 空值缓存 +(必要时)布隆过滤器"三层递进;击穿用"互斥/合并 + 逻辑过期"二选一或并用;雪崩用"TTL 抖动 + 多级缓存 + 限流降级"。来源:阿里云开发者社区、掘金实践、VergeCloud。
- 布隆过滤器不是免费午餐:误报、无法删除(Cuckoo Filter 弥补)、数据更新后需重建;社区建议"资源有限或脏数据少时优先空值缓存,海量不存在 key 请求时才上布隆"(cnblogs)。
- singleflight 是击穿防御的性价比之王(单实例场景):官方语义就是"对同一 key 保证只有一个执行在途,重复调用者等待并共享结果",是 Go 生态处理击穿的事实标准;.NET HybridCache 把同一语义做成默认行为(pkg.go.dev、GA 公告)。
- XFetch 提供理论最优但工程采用率低:2015 年论文证明了概率提前过期在"浪费的提前重建"与"击穿概率"之间的最优权衡,零协调、无需锁,但需要预知回源耗时且参数难解释;实战中 singleflight + TTL 抖动仍是绝对主流,直到 2024 年 Cloudflare 才将其落地生产(XFetch 论文、Kit 工程实践、Cloudflare: Sometimes I cache)。
选型总览:三层一张表
| 层 | 选项 | 一句话定位 | 默认推荐(2026-11 截面) |
|---|---|---|---|
| 引擎 | Redis 8 | 参考实现 + 最全能力 | ✅ 一般推荐(注意 AGPLv3 合规) |
| 引擎 | Valkey 9 | 开源 + 云厂商对齐 | ✅ 想规避 Redis 许可纠结时 |
| 引擎 | Dragonfly | 多核重吞吐重写 | 仅在大单机热点时 |
| 引擎 | KeyDB / Memcached | 存量维护 | 新项目不再首选 |
| 引擎 | Garnet | .NET 栈试验田 | .NET 且多核单机时 |
| 库 | Caffeine | JVM L1 默认解 | ✅ JVM 项目 |
| 库 | Otter/Ristretto | Go L1 | ✅ Go 项目(先看维护活跃度) |
| 库 | HybridCache | .NET 双层+防击穿内建 | ✅ .NET 项目 |
| 模式 | Cache-Aside + TTL 抖动 | 写路径简单的最终一致 | ✅ 一切读多写少场景 |
| 模式 | singleflight / 逻辑过期 | 击穿防御 | ✅ 默认 singleflight;上游容忍旧值时叠加逻辑过期保体验 |
| 模式 | 空值缓存 + 参数校验 | 穿透防御 | ✅ 默认;布隆过滤器按需 |
| 模式 | XFetch | 理论最优提前刷新 | 量化控团队可选 |
三、交叉洞察:两轴结合说明了什么
把纵轴的"从哪来"与横轴的"现在在哪"交叉,能得到六条单轴得不到的判断:
洞察一:回源代价决定缓存形态,且每次变贵都重演同一套防御
纵轴的主线是"回源代价"的三次跃迁:回源到数据库(memcached 时代)→ 回源到第三方 API(Cloudflare Workers 时代)→ 回源到按 token 计费的 LLM(GPTCache 时代)。横轴印证了其结构同构性:布隆过滤器防的是"不该到库的请求",语义缓存防的是"不该到模型的 token"——防御的对象换了,雪崩/穿透/击穿的分类框架原封不动。判断任何新缓存场景(如 Agent 工具调用结果缓存)时,可以直接套用这套分类学与工具箱,不必重新发明。
洞察二:模式比引擎稳定一个数量级——这是两轴交叉给出的最重要结论
横轴显示:2024–2025 两年间,许可证大战把引擎市场彻底重排(Redis 弃 BSD → Valkey 分叉 → Redis 8 以 AGPLv3 回摆),六款引擎各有兴衰;但 cache-aside、TTL 抖动、单飞合并、空值缓存这些模式横跨所有引擎原样成立。纵轴给出同样的证据:memcached → Redis → Valkey 引擎换了三代,cache-aside 自 2003 年定型至今从未被替代。推论:工程实践的精力应优先投在模式与纪律上;引擎选型是可逆决策,模式选型不是。(这一结论由横轴的引擎兴衰对比与纵轴的 cache-aside 跨代存续证据共同支撑。)
洞察三:击穿防御的演化方向是"去中心化 + 下沉到库",手写分布式锁是过渡形态
纵轴的演化链:缓存锁(nginx 1.1.12,2011)→ 租约(Facebook NSDI'13)→ 单飞(singleflight 2013、Spring sync 2016)→ 理论最优概率提前过期(XFetch 2015,Cloudflare 2024 落地)——从"互斥"走向"无锁概率"。横轴补上当下坐标:HybridCache 把合并语义做成默认开启,Valkey 9 的 hash 字段级过期把失效粒度做成引擎原生。两轴连起来看,趋势是防御从"应用自己写"逐级下沉为"库的默认能力"再下沉为"引擎原生能力"。推论:新项目里手写分布式锁防击穿的教程代码会越来越少;选库时把"是否内建防击穿合并"当作默认考量项。(RedLock 之争提醒了下沉的边界:Kleppmann 2016 年的批评仍然有效,分布式锁不是免费的正确性。)
洞察四:理论最优 ≠ 工程主流,标准化慢于实践——采用率由工程成本决定
XFetch 是最好的标本:2015 年就有理论最优解、零协调、无需锁,但因为需要预估回源耗时、参数难解释,实战中 singleflight + TTL 抖动一直占绝对主流,直到 2024 年 Cloudflare 才找到大规模落地的形态。同理,RFC 5861(2010)定义 stale-while-revalidate,到 CDN 完全异步实现(2026-02-26)隔了整整 16 年。两轴交叉说明:一个防御手段能否普及,取决于可解释性、可调参与实现成本,而不是理论优美度;同时要接受"标准与实践之间存在十年量级的时差"。
洞察五:中小规模下引擎性能差异是伪问题,一致性失效才是真问题
横轴的引擎对比显示 Dragonfly/Garnet 吞吐领先是真实的,但触发条件是多核大内存高并发;纵轴的 Redis 6.0 多线程 I/O 演化同样说明,单线程瓶颈只在超大规模出现。绝大多数服务(更不必说个人服务)的流量远未触及 Redis 单线程模型的吞吐上限。而 cache-aside 的脏读窗口、miss 竞态、失效传播,是每一个用缓存的项目都必须面对的。选引擎的真正变量是许可证、托管可用性与运维熟悉度,而非吞吐数字;选模式的真正变量是数据能容忍多旧。
洞察六:L1+L2 成为新默认架构,而防御的另一半在"收敛输入";极限处承认失效之难
纵轴上 Caffeine(2015-12)与横轴上 HybridCache(.NET 9 GA)合起来,说明进程内缓存已从"dirty hack"升级为一等公民:L1 天然吸收热点与 L2 故障(雪崩与宕机兜底),库内建合并天然吸收击穿——"L1+L2"成为新默认架构。另一半防御在"收敛输入":参数校验、空值缓存、布隆过滤器解决的都是"不该到库的请求到了库",把缓存问题前移成 API 治理问题。最后承认极限:缓存失效永远难——从 Phil Karlton 的名言到 Facebook 的 remote markers,再到 canal 的 binlog 订阅,人类不断用更重的基础设施回避"本地删除到底够不够"这个问题;个人服务的答案便宜得多:接受最终一致,让 TTL 做最后一道保险。
对用户的判断与建议(附两章合并后的个人服务落地清单)
三条总判断:
- 先问要不要,再问怎么防:静态/半静态内容直接上 CDN/反向代理缓存,不进应用层;读多写少才值得 cache-aside;强一致数据(支付、余额)直接绕过缓存查库——这比任何失效策略都便宜。
- 三件套覆盖三类事故的主要攻击面:TTL 随机抖动(防雪崩)、进程内单飞(防击穿)、参数校验+空值缓存(防穿透)——零依赖、一天内可落地。
- 默认不上的两样东西:分布式锁(个人/中小服务并发量到不了,复杂度与故障面不成比例,且 Kleppmann 的 RedLock 批评适用)与布隆过滤器(key 空间不大时,重建与误判成本高于收益)。
个人服务落地清单(由纵轴与横轴两章各自的落地清单合并而成,消除了两处表述差异:缓存引擎统一为 Redis 8 / Valkey 9 当下版本;淘汰策略统一为"缓存场景优先 allkeys-lfu,简单默认可用 allkeys-lru"):
- 【选型·第 0 步】内容静态或半静态(博客、文档站)?→ 不需要应用层缓存,直接 CDN/反向代理缓存(如 Cloudflare 免费档)。
- 【选型】单进程先不引 Redis:本地字典 / Caffeine / Python
cachetools已足够;确需跨进程共享,再上单机 Redis 8 或 Valkey 9(Docker 跑在 VPS 上;个人自用推荐 Valkey——BSD 许可,无商用授权焦虑),maxmemory设为实例内存的 1/3,淘汰策略缓存场景优先allkeys-lfu(Redis 4.0+ 内建,抗偶发扫描污染),简单默认allkeys-lru亦可。 - 【选型·托管】完全不想运维(Vercel/Workers 等平台部署)?→ Upstash Redis 免费档:每月 50 万条命令 + 250MB,超出后按 $0.20/10 万条计费(Upstash 定价、定价公告);按命令计费对抖动负载不可预测,给消费设上限。
- 【旁路缓存标准化】读 = 查缓存 → miss → 查库 → 回填 TTL;写 = 更新库 → 删除缓存键(不要更新缓存、不要先删缓存再更新库)。TTL 取"业务可容忍陈旧度"的 1/2–1/3。平面 C-1 风险列提到的"旧值回填、并发写竞态",个人服务可接受的理由:写并发低,竞态窗口只有一次回源耗时(毫秒级),且最坏结果被 TTL 上限框住——TTL 本身就是兜底;在出现"多实例高频写同一 key"或"读方对秒级旧值敏感"的信号之前,守住这一条即可,无需延迟双删等升级手段。
- 【防雪崩】TTL 一律加随机抖动:
ttl = base ± rand(0, base*0.2);批量预热不要让所有 key 同秒过期。 - 【防击穿】进程内单飞:Go 用
golang.org/x/sync/singleflight;Java 用 Caffeine LoadingCache 或@Cacheable(sync=true);.NET 直接用 HybridCache(内建、默认开启);Python 用 per-keyasyncio.Lock。互斥范围到"本进程"即可,不要为个人服务引入 Redis 分布式锁。 - 【防穿透】参数校验前置(id 范围/格式,非法直接 400)——这是免费的第一道闸;miss 时缓存空值(短 TTL 30–60s)。布隆过滤器默认不上,仅当 (a) 遭受真实恶意扫描且 (b) key 集合稳定可重建时,才用 Redis 8 原生
BF.ADD/BF.EXISTS。 - 【保体验】宁可返回旧值:逻辑过期/后台刷新(SWR 思想)——命中时判断"已软过期"→ 返回旧值并异步刷新;统一口径(与选型总览一致):默认 singleflight;上游容忍旧值时叠加逻辑过期保体验。
- 【内存有界】Redis 设
maxmemory+ 淘汰策略;本地缓存设容量上限(如 CaffeinemaximumSize)。缓存把宿主机内存吃爆,等于自造一场雪崩。 - 【脏读兜底 = TTL】接受"缓存内是旧数据"这一事实,TTL 是一切失效逻辑失败时的最终一致性保障;据此给每个 key 设 TTL 上限。绝大多数读多写少的数据对秒级旧值无感;强一致数据绕过缓存。
- 【降级路径】缓存挂了不等于服务挂了:读路径 try-catch 后回源 + 限流(一个简单的信号量/计数器即可)+ 进程内兜底 TTL 缓存;写一个"缓存不可用"开关并实际演练一次,避免缓存故障放大为数据库故障。
- 【可观测】必看的四个数:命中率(Redis
INFO的keyspace_hits/keyspace_misses)、evictions(evicted_keys)、回源 QPS/P99 延迟、缓存层错误计数——打到日志即可,不必上 APM。没有命中率统计的缓存等于没有缓存。 - 【热点预案】个人服务单实例基本无热 key 问题;若多实例,热 key 用"本地短 TTL(1–5s)一级缓存 + Redis 二级"双层即可,无需 JD-hotkey 级别的框架。
- 【演练一次雪崩】上线前用 wrk/vegeta 以"全部缓存为空"的冷状态压测一遍,确认数据库在 miss 风暴下不挂——这是个人服务唯一一次值得做的压测。
四、参考来源
以下链接均来自两份章节的撰写当日检索验证(少数为本终稿修订时补验);folklore 类(Phil Karlton 名言、延迟双删等)已明确标注,不冒充文献。
起源与标准
- memcached 起源:Wikipedia: Memcached · Ars Technica 十周年回顾 · memcached.org/about
- Redis 起史:Wikipedia: Redis · antirez/historical-redis-versions · antirez《The end of the Redis adventure》
- HTTP 缓存标准:RFC 2616 · RFC 7234 · RFC 5861(stale-while-revalidate) · Cloudflare async SWR changelog(2026-02-26) · Cloudflare Community(2022 非异步承认)
- 术语与母概念:Wikipedia: Cache stampede · Thundering herd problem · Bloom filter · Squid)
论文与工业实践
- Facebook 旁路缓存体系:Scaling Memcache at Facebook (NSDI'13)
- 击穿理论解:XFetch: Optimal Probabilistic Cache Stampede Prevention (2015) · Wikipedia: Cache stampede(XFetch 伪代码) · Cloudflare: Sometimes I cache (2024-12-26) · Go 概率提前过期实践 · Kit 工程实践
- 单飞与请求合并:groupcache singleflight · pkg.go.dev/singleflight · nginx proxy_cache_lock · Simon Willison
- 框架内建防御:Spring 3.1 GA · Spring 3.1 M1 Cache Abstraction · Spring 4.3 sync
- 本地缓存演进:TinyLFU arXiv:1512.00727 · Caffeine · Caffeine 基准 wiki · Design of a Modern Cache (High Scalability) · SPARK-34309 · Ristretto · Otter 博客
- Netflix:Caching for a Global Netflix (EVCache) · Evolution of Application Data Caching: From RAM to SSD (2018) · Netflix TechBlog caching 专题
- 其他工业实践:Twitter Twemproxy · 京东 JD-hotkey 设计与实践 · 得物热 key 方案
- 分布式锁之争:Kleppmann: How to do distributed locking (2016) · antirez: Is Redlock safe?
引擎层现状(2024–2026)
- Redis:7.4 双许可公告(2024-03-20) · AGPLv3 公告 · LWN 报道 · Redis 8 GA · What's New 8.0 · 布隆过滤器文档 · Redis 4.0(LFU) · Redis 6.0(多线程 I/O) · InfoQ: Redis AGPL · Redis Cluster 3.0 发布帖
- Valkey:Linux Foundation 新闻稿(2024-03) · A Year of Valkey · 内存效率 8.0 · 8.1 GA · 9.0 · LF 9.0 新闻稿 · AWS ElastiCache for Valkey 8
- 其他引擎:Dragonfly shared-nothing · Dragonfly features · Dragonfly: Valkey vs Redis 指南 · Garnet (MSR) · Garnet 文档 · Garnet VLDB 论文 · LWN: The evolution of Memcached · Memcached 维护文档 · endoflife.date/memcached · Memcached 1.6.25 发布说明 · KeyDB 被 Snap 收购(TechCrunch, 2022-05-12)
- 第三方基准(未复测,定性参考):centminmod · RepoFlow · DBPro · PkgPulse · FastAPI 百万次实测 · Danube Data
模式与防御
- 模式权威:Azure Cache-Aside Pattern · Azure 缓存指南 · AWS ElastiCache 缓存策略 · AWS 缓存最佳实践 · Hazelcast 缓存模式指南 · Redisson 术语表 · Redis 官方缓存策略博客 · .NET HybridCache 文档 · .NET 缓存总览 · HybridCache GA 公告
- 前端 SWR:Vercel SWR · vercel/swr(GitHub,2019 开源)
- 防御专题:OneUptime: Redis Cache Stampede · VergeCloud · Scalable Thread: Dogpile problem
- 中文社区:美团《缓存那些事》(2017-03-17) · 阿里云:《Java 开发手册》前世今生 · 凤凰架构 · 小林 coding:缓存问题 · 阿里云开发者社区:穿透防御 · 掘金实践 · cnblogs 专题 · 掘金:热 key 方案对比
- AI 缓存:GPTCache (Zilliz, 2023-04) · ACL NLP-OSS 2023 论文 · GPTCache GitHub
- canal:github.com/alibaba/canal
个人服务成本