Skip to content

第 22 周 · Day 2:缓存三件套——把拆过的雷翻译成面试话语体系 ​

对应手册任务:学习「缓存三件套:穿透/击穿/雪崩」,动手「针对你的平台,写出每个问题的具体防护方案」,当日产出「缓存设计文档」。本篇只解决一个问题:第 6 周 Day 2 你在 /users 接口上拆过这三颗雷,第 21 周 Day 6 又在 pgvector 上重新拆了一遍,但会做不等于会讲——今天把它们翻译成面试官要的结构化答案,再落到 AI 客服平台的真实场景,产出一份拿得出手的设计文档。

今日目标 ​

  1. 三件套每套都能讲两个版本:30 秒版(名词准确、互相区分)和 2 分钟版(现象→原因→2-3 个解法→各自取舍)
  2. 把三件套落到自己的平台:超纲问题的穿透、热门 FAQ 的击穿、语义缓存的雪崩,每个都有选定方案,还说得出为什么不选另一个
  3. 独立写出缓存设计文档:场景、选型、三件套对策、一致性策略、监控指标五段齐全,经得起面试官五层追问

概念讲解:为什么面试第一个问缓存 ​

系统设计面试有个不成文的剧本:候选人一说"加个缓存",面试官几乎必然接一句"缓存会出什么问题"。三个答案等在那里——穿透、击穿、雪崩。这不是刁难,是分工:你说缓存是加速器,他要确认你知道缓存也是故障放大器。

原因很实际。缓存是所有中间件里最容易"加上了、跑通了、看起来没问题"的一个:平时流量低,三种故障一个都不现身,压测上量或流量来潮才集体冒头。所以这三道题成了最便宜的试纸——背过名词的人张口就是"布隆过滤器、互斥锁、TTL 加随机",做过的人会先讲故障剧本:谁在什么时刻干了什么,系统怎么一步步垮掉。面试官听的就是这个剧本。

这个剧本你写过。第 6 周 Day 2 给 /users 列表加缓存那天,空值缓存、TTL 抖动、互斥锁的原料全都碰过;第 21 周的语义缓存又把战场从 Redis 搬进了 pgvector。今天只做翻译:把"我写过这段代码"翻译成"我设计过这个系统"。翻译有一条纪律,背结构,不背稿。结构是"现象→原因→解法与取舍",稿是某篇文章的原话;结构能套任何场景,稿换个场景就死机。

核心知识 ​

本节没有新代码——代码第 6 周和第 21 周都写完了,今天要写的是话术和方案。

1. 三十秒版本:先把三个名词说准 ​

面试官抛出"缓存三件套",第一轮只要名词加一句成因。这 30 秒的作用是把清单快速过掉,递话头给他挑一个深挖:

  • 穿透:查"根本不存在"的数据,缓存永远不会有答案,每一发都直通数据库。解法:空值缓存、布隆过滤器。
  • 击穿:某个热点 key 过期的瞬间,并发请求整体回源。解法:互斥重建、逻辑过期。
  • 雪崩:大批 key 同一时刻集体过期,或缓存层整体宕机。解法:TTL 抖动、高可用部署、限流降级。

区分的口诀第 6 周就背过,面试原样能用:穿透是"无中生有",击穿是"趁虚而入",雪崩是"成批倒下"。穿透和击穿最容易混,一句话钉死:穿透查的数据压根不存在,击穿查的数据存在、只是缓存恰好过期。

2. 两分钟版本:每个解法都带取舍 ​

面试官挑中一个深挖时,答案固定四步:现象(故障剧本)→ 原因 → 两三个解法 → 每个解法的代价。下面三个两分钟版本全落在 AI 客服平台上,换场景就照结构现场重推。

穿透:用户问超纲问题。 剧本:有人问"你们老板工资多少",知识库里没有这份文档,精确缓存的 key 是问题原文,永远 miss;语义缓存里也找不到相似意图。这一发请求走完整条最贵的链路:Embedding、向量检索、LLM 生成兜底话术。要是脚本刷一万个不同的超纲问题,LLM 账单直接起飞。原因:缓存只能记住"查过的存在数据",对"不存在"没有记忆点。解法一,空值缓存:把"知识库答不了"这个事实本身缓存起来。平台的特殊之处在语义层——超纲问题千奇百怪,逐字缓存挡不住变体,所以做成语义化的空值缓存:把兜底话术连同它的向量写进缓存表,下一个相似的超纲问题命中后直接返回,连 Embedding 都省了。代价是知识库更新后,空值会短暂挡住新答案,所以 TTL 必须短,分钟级起步。解法二,布隆过滤器:前置一个只答"可能存在/一定不存在"的概率结构。它要求 key 集合有限且相对固定——商品 ID 这种封闭集合合适,"用户的提问"这种无限开放的集合不合适:问法无穷多、文档天天新增,过滤器得不停重建,误判还会把新问题拦在门外。结论:平台选语义化空值缓存,布隆留给"文档 ID 存在性校验"这种封闭集合。"为什么不选布隆"这段讨论比背下布隆原理更加分——解法不是越多越好,是说清取舍。

击穿:热门 FAQ 过期的瞬间。 剧本:第 21 周压测的配比里,三成流量是反复问的固定题,"怎么退款"这类问题靠 KV 预缓存层(第 21 周 Day 6 说的那层"原文相同直接返回")挡在模型外面。这一层的 TTL 归零那一毫秒,几百个并发同时 miss、同时走全链路,LLM 并发额度瞬间打满,其他用户全部排队。原因:热 key 的过期成了并发回源的扳机。解法一,互斥重建:回源前抢一把 SET NX PX 的锁,只放一个请求进链路,其余的等结果。代码最简单,原料第 6 周 Day 3 攒过;代价是等待者延迟上升,锁持有者挂了要等锁的 TTL 自然释放。解法二,逻辑过期:key 不设物理 TTL,过期时间写进 value,读到"已逻辑过期"先返回旧答案,同时异步刷新。零等待、永不打穿;代价是 value 结构要改,还会短暂返回旧内容。对客服 FAQ 这个取舍毫无悬念:答案偶尔才更新,用户不会为三分钟前的退款话术投诉,但一定会为排队八秒投诉——选逻辑过期。判断口诀:业务能不能容忍"短暂的旧",能就逻辑过期,不能就互斥重建。

雪崩:缓存层成批倒下。 剧本两种。自己埋的雷:运营凌晨批量导入一万条 FAQ 预热,全设同一个 TTL,三十分钟后整批同时过期,miss 洪峰整体砸向 LLM。天降的祸:Redis 或 pgvector 宕机,缓存层整体消失,所有提问走全链路。对别的系统,雪崩打垮的是数据库;对 LLM 应用,雪崩烧的是钱——每一次 miss 都是按 token 计费的真金白银。解法一,TTL 抖动:第 6 周的公式原样搬来,基准值加 Math.random() 乘抖动区间,把过期时间打散;语义缓存的 TTL 藏在缓存行的创建时间里,查询时算年龄,效果相同。解法二,高可用:Redis 上哨兵或集群,pgvector 挂掉时应用降级而不是全站 500。解法三,平台特色:多模型降级——缓存失守带来的 LLM 洪峰,正好撞上第 21 周的路由器和熔断,命中率跌破阈值时把流量切向小模型和标准兜底话术,成本曲线的尖峰被削平。三个解法的顺序就是答题顺序:先防同时过期,再防整体宕机,最后兜住漏到下游的流量。

3. 升级话题一:一致性——预演面试官的五层追问 ​

三件套答完,追问链多半拐向"写的时候缓存怎么办"。标准答案"先更新库,再删缓存"只是第一层,往下还有四层,每层都得接得住。

第一层:为什么先更库再删缓存?反过来窗口更大——先删缓存、库还没改完的空档里,并发读回源拿到旧值并回填,旧数据驻留缓存直到 TTL 到期,读请求长期拿错的。先库后删,不一致窗口只剩"库已更新、缓存还没删"一小段。

第二层:那这样就一致了?没有。窗口期内的读请求拿到的还是旧值。

第三层:最坏能坏到什么程度?极端时序:读请求先出发,在库更新前读了旧值,回填动作慢;写请求后出发,更新库、删缓存(此时缓存还没有那个 key,删了个空气);慢读随后把旧值回填。旧值从此驻留到 TTL 到期。这要求"读比写先出发却后到达",概率极低,但存在。

第四层:怎么兜底?TTL 是不一致的上界,最坏错一个周期,不会永久错。要更强就延迟双删:写完睡几百毫秒再删一次,把慢读回填的旧值清掉。再强就订阅数据库 binlog(Canal 这类)异步删缓存,把"删"从业务代码挪出去,失败还能重放。

第五层:删缓存失败了呢?重试进消息队列,或靠 binlog 下游重放,最终一致。

应对的总原则:答到"概率极低、但存在,上界是 TTL"就停,别逞强说"绝对不会不一致"。主动承认权衡不是示弱,是这道题的满分姿态。

4. 升级话题二:多级缓存——层次与失效联动 ​

平台现在实际跑着三层缓存,面试里画出层次图很加分:

  1. 本地缓存(进程内 LRU):挡最热的字面量——热门 FAQ 的原文、系统配置。亚微秒级,但多实例各自一份,写操作没法精确失效,这台机删了,那台机不知道。
  2. Redis:业务数据的精确缓存——会话状态、token 黑名单(第 6 周 Day 1 迁过去的那批)、用户资料。
  3. 语义缓存(pgvector):按意图命中,服务"同一个意思的无数种问法"。

层次的规律是第 6 周那句"越聚合的缓存越难精确失效"的推广:越靠近应用的层,失效越难联动,TTL 就要越短、越只放"错了也无所谓"的数据。本地缓存放秒级的配置和 FAQ 副本;Redis 放分钟级业务数据;语义缓存跟知识库文档版本走——重建索引时 bump 版本号,旧缓存按版本整体作废。

动手任务:缓存设计文档一步一步 ​

手册任务:针对你的平台,写出每个问题的具体防护方案。拆成 5 步,全程约 40 分钟,产出 markdown 文件 cache-design.md,建议和Day 1 的架构文档放在一起。

第 1 步:建文件,搭五段骨架。 标题写"AI 客服平台缓存设计",下面五个空节:场景与选型、三件套对策、一致性策略、多级缓存结构、监控指标。今天剩下的时间就是把这五个空节填满。

第 2 步:写场景与选型。 盘点平台所有缓存点,一张表列完,每行四列:数据、现居哪层、TTL、为什么值得缓存。至少包含会话状态、token 黑名单、用户资料、FAQ 字面量、语义缓存行。最后一列是给自己出的题——写不出收益的数据就别缓存,缓存是有维护成本的。

第 3 步:写三件套对策。 每个问题一小节,固定格式:故障剧本(谁、什么时刻、系统怎么垮)→ 选定方案 → 不选的方案及原因 → 关键参数。参数必须落数字:空值缓存 TTL 给 30 到 60 秒,TTL 抖动给"基准 300 秒、抖动 60 秒"(第 6 周的原值),逻辑过期的刷新阈值给 5 分钟。没有数字的设计文档没法评审。

第 4 步:写一致性策略。 按数据分档:哪些数据允许旧多久(FAQ 允许分钟级,余额类一分钟都不行)、写路径口诀(先更库再删缓存)、兜底手段(TTL 上界、哪些数据值得延迟双删)。把上一节五层追问的答案浓缩成三行写进去——面试被问到时,它就是你的提词器。

第 5 步:写监控指标,再自测一遍。 指标至少五个:各层命中率(语义缓存按第 21 周压测画像,预期 30%-60%)、回源 QPS、LLM 调用次数(llm_usage 表现成可查)、P99 延迟、缓存层错误率。写完把五层追问当考卷,对着文档自问自答,答不上的回本篇对应小节补课。

常见踩坑 ​

坑 1:背稿不背结构。 把某篇答案一字不差背下来,面试官换个场景("电商秒杀怎么防击穿")就死机。稿是内容,结构才是能力:现象、原因、解法、取舍四步走,任何场景现场重推。自检办法:把答案里的"客服"全部换成"商品",还能不能顺下来。能,说明背的是结构。

坑 2:解法名单背得溜,取舍一句说不出。 "互斥锁还是逻辑过期?"必须有下文:"看业务能不能容忍短暂的旧数据。"这句接不上,前面背的所有解法都白费——面试官问三件套,问的是权衡,不是名词量。

坑 3:把布隆过滤器当万能药。 什么穿透都答布隆,追问一句"客服的问题每天新增十万条,过滤器怎么办"就卡住。布隆的适用条件(集合有限、相对固定)比它的原理更该背。说不清"为什么不用",等于没用过。

坑 4:一致性答过头。 说了"先更库再删缓存"就补一句"这样就一致了",等于亲手把下一层追问的靶子递过去。正确姿态是主动说出窗口期和极端时序,再给出 TTL 上界。这道题考的就是你会不会把"最终一致"说成"绝对一致"。

坑 5:设计文档写成解法堆砌。 五段全是方案名词,没有一个场景、一个数字,这不叫设计文档,叫名词表。评审的人和面试官要看的是:什么数据、什么流量、错多久能接受、怎么知道它出事了。每个小节末尾放一个数字,文档立刻从摘抄变成设计。

自测问题 ​

先自己答,再展开对照。答不上来的,回对应小节看一遍。

  1. 30 秒内说清三件套,并各用一句话互相区分。
参考答案

穿透查不存在的数据,缓存永远不命中;击穿是热 key 过期瞬间的并发回源;雪崩是大批 key 同时过期或缓存层宕机。口诀:无中生有、趁虚而入、成批倒下。穿透与击穿的分界:查的数据到底存不存在。

  1. 互斥重建和逻辑过期怎么选?结合客服 FAQ 给出结论和理由。
参考答案

看业务能否容忍"短暂的旧"。FAQ 答案偶尔更新,用户不会为三分钟前的退款话术投诉,但会为排队八秒投诉,所以选逻辑过期:先返回旧答案,异步刷新。反过来,余额、库存这类旧一秒都不行的数据,选互斥重建,宁可让请求等。

  1. 布隆过滤器为什么不适合"用户提问"这个 key 集合?什么场景它才合适?
参考答案

布隆的前提是 key 集合有限且相对固定,这样误判率才可控、重建才不频繁。用户提问无限开放、每天海量新增,过滤器要不停重建,误判还会拦掉新问题。合适的是封闭集合,比如文档 ID、商品 ID 的存在性校验——集合稳定,误判只多花一次回源。

  1. "先更新库再删缓存"的极端不一致时序是什么?靠什么兜底?
参考答案

读请求先出发,在写请求更新库之前读了旧值,回填动作慢;写请求更新库、删缓存(缓存里还没有那个 key,删了个空气);慢读随后把旧值回填,旧值驻留到 TTL 到期。兜底三件:TTL 做不一致的上界,延迟双删清掉慢读的回填,binlog 订阅异步删保证删除必达。

  1. 多级缓存里,越靠近应用的层,TTL 为什么越短?
参考答案

越靠近应用(本地缓存),实例越多、失效越难联动——单机的删除广播不到别的实例,写路径够不着它。失效不可靠的层只能靠短 TTL 自愈,所以只放"错了也无所谓"的秒级数据:热点字面量、配置。Redis 层可精确删除,放分钟级业务数据;语义缓存跟文档版本走,版本号一 bump 整体作废。

延伸阅读 ​

  • 小林 coding:图解 Redis,三件套画成了三张图,面试前把图记熟,30 秒版本直接从图里长出来
  • Scaling Memcache at Facebook(NSDI 2013),工业界应对热 key 与不一致的第一手材料,lease 机制就是冲着击穿去的,追问链接不住时读它
  • GPTCache,开源 LLM 语义缓存,第 21 周 Day 6 手写那套的产品化版本,重点看它的失效策略怎么设计

今天的产出 cache-design.md 留好。五段骨架(场景/选型/对策/兜底/监控)明天设计队列方案时原样复用——系统设计面试里,文档的结构比内容更可迁移。