Files
examination/topics/architecture/cache-system/cache-three-problems/short_answer.json
T
2026-09-05 12:03:21 +08:00

239 lines
12 KiB
JSON

{
"topic": "cache-three-problems",
"type": "short_answer",
"schema_version": "1.0.0",
"generated": "2026-09-05T00:00:00+08:00",
"questions": [
{
"id": "sa-001",
"type": "short_answer",
"difficulty": 2,
"tags": [
"缓存穿透"
],
"question": "什么是缓存穿透?它的根本危害是什么?",
"answer": "缓存穿透指查询一个数据库中也【不存在】的数据。缓存查不到、DB 也查不到,无法写回缓存,导致每一次请求都直达 DB。其根本危害是:反复回源会对数据库造成持续高压,恶意攻击者用大量不存在的 ID 请求即可把 DB 打垮。",
"keywords": [
"不存在",
"每次请求",
"直达",
"数据库",
"打垮DB",
"穿透"
],
"scoring_rubric": "正确给出定义(查询不存在的数据)得 40 分;说明缓存无法写回导致每次直达 DB 得 30 分;点出对 DB 持续高压的危害得 30 分。",
"explanation": "穿透的本质是「没数据可缓存」,所以常规 miss→回源流程失效,必须前置拦截。",
"source": null,
"related": []
},
{
"id": "sa-002",
"type": "short_answer",
"difficulty": 2,
"tags": [
"缓存穿透",
"参数校验",
"布隆过滤器",
"缓存空对象"
],
"question": "缓存穿透的常见解决方式有哪些?请至少列出三种并简述原理。",
"answer": "1) 参数校验:在入口拦截非法/明显不存在的参数;2) 缓存空对象:DB 查不到也缓存 null + 极短 TTL,防止同一 key 反复穿透;3) 布隆过滤器:请求先过布隆过滤器,判断不存在直接拦截不查 DB(无假阴性);4) 还可以在数据库层做兜底校验等。",
"keywords": [
"参数校验",
"缓存空对象",
"布隆过滤器",
"空值",
"短TTL",
"拦截"
],
"scoring_rubric": "答出每种正确方案得 30 分(共可答多但至少三种得 90 分);能说明每种原理(为何能防穿透)再得 10 分。",
"explanation": "穿透的关键是「根本不存在」,所以必须前置拦截(参数校验、布隆)或对空结果做缓存(空对象)避免重复回源。",
"source": null,
"related": []
},
{
"id": "sa-003",
"type": "short_answer",
"difficulty": 1,
"tags": [
"缓存击穿"
],
"question": "简述缓存击穿的定义与它的典型场景特征。",
"answer": "缓存击穿指一个特别热门的热点 key(如微博热搜)缓存刚好过期的瞬间,大量请求同时 miss 全部打到一个数据库。特征是【单 key、高并发、瞬时尖峰】——只有那一个 key 失效,却因并发极高而冲垮 DB。",
"keywords": [
"单个热点key",
"过期",
"高并发",
"瞬时尖峰",
"打数据库",
"单key"
],
"scoring_rubric": "正确界定为单个热点 key 过期得 50 分;写出大量请求同时回源打 DB 得 30 分;总结单 key/高并发/瞬时尖峰特征得 20 分。",
"explanation": "击穿针对的是「单 key」,这一点是与雪崩(多 key)最关键的区分。",
"source": null,
"related": []
},
{
"id": "sa-004",
"type": "short_answer",
"difficulty": 2,
"tags": [
"缓存击穿",
"互斥锁"
],
"question": "用互斥锁解决缓存击穿的思路是什么?请说明加锁与不加锁的区别。",
"answer": "思路:当热点 key 过期且多个请求同时请求时,只有【一个请求拿到锁(如 SETNX)去数据库回源并回填缓存】,其他请求阻塞等待锁释放后命中缓存,或直接返回旧值/降级。区别:不加锁时 N 个请求会同时打 DB 造成数据库瞬时峰值;加锁后同一时刻只有一个请求回源,其余请求等待或命中缓存,从而把峰值压平,保护 DB。",
"keywords": [
"互斥锁",
"SETNX",
"只有一个回源",
"其余等待",
"命中缓存",
"保护数据库"
],
"scoring_rubric": "正确说明只有一个请求回源并回填得 50 分;解释其余请求等待/命中缓存得 30 分;说明加锁与不加锁的区别得 20 分。",
"explanation": "互斥锁的核心是把「N 个线程同时回源」收敛为「1 个线程回源 + 其余等待」,通过牺牲少量延迟换取数据库安全。",
"source": null,
"related": []
},
{
"id": "sa-005",
"type": "short_answer",
"difficulty": 3,
"tags": [
"缓存击穿",
"逻辑过期"
],
"question": "说明「逻辑过期」方案解决缓存击穿的原理,并指出它相比真删除的一个关键优势。",
"answer": "逻辑过期不物理删除 key,而是在 value 中额外记录一个逻辑过期时间。请求发现逻辑过期后不直接删除,而是由后台异步线程去更新数据并重设缓存;期间返回的仍是旧值持续可用。关键优势:在缓存「看似已过期」到「后台刷新完成」的窗口内,请求依旧能命中缓存而无需全部穿透到 DB,从而挡住并发瞬时冲击。",
"keywords": [
"逻辑过期时间",
"后台异步",
"异步线程",
"旧值可用",
"不删缓存",
"挡并发"
],
"scoring_rubric": "正确描述不删除 key 且记录逻辑过期时间得 40 分;说明由后台异步线程刷新得 30 分;写出返回旧值/持续可用、挡住并发 的认知得 30 分。",
"explanation": "逻辑过期用「数据可用性优先」避免窗口期打 DB,代价是可能短暂读到旧数据,适合对实时性要求不极端的场景。",
"source": null,
"related": []
},
{
"id": "sa-006",
"type": "short_answer",
"difficulty": 3,
"tags": [
"缓存雪崩",
"TTL加随机"
],
"question": "缓存雪崩的成因有哪些?分别说明其对应的治理思路。",
"answer": "成因一:一大批 key 同时设置了相同过期时间,到了同一时刻集体失效;对应思路——给 TTL 加随机偏差打散到期时间。成因二:缓存服务整机宕机导致所有请求穿透;对应思路——多级缓存兜底、加锁/限流/熔断降级保护 DB,以及 Redis 主从+哨兵+集群的高可用部署。",
"keywords": [
"相同过期时间",
"集体失效",
"加随机",
"宕机",
"多级缓存",
"限流降级",
"高可用"
],
"scoring_rubric": "正确识别集体失效并给出加随机策略得 40 分;正确识别宕机并给出兜底/限流/降级得 35 分;给出 Redis 高可用部署得 25 分。",
"explanation": "雪崩是「批量」失效叠加「整机宕机」两大风险,治理必须组合 TTL 打散 + 多层兜底 + 限流降级 + 高可用。",
"source": null,
"related": []
},
{
"id": "sa-007",
"type": "short_answer",
"difficulty": 1,
"tags": [
"缓存穿透",
"缓存击穿",
"缓存雪崩"
],
"question": "缓存穿透、击穿、雪崩三者的核心区别是什么?",
"answer": "穿透——根因是查询【不存在】的数据,缓存永远 miss,DB 持续高压,解法是空值缓存+布隆过滤器+参数校验;击穿——单个热点 key 刚过期遭遇高并发,DB 瞬时尖峰,解法是互斥锁+逻辑过期;雪崩——大批 key 同时过期或缓存宕机,多 key 集体失效冲垮 DB,解法是 TTL 加随机+多级缓存+限流降级+Redis 高可用。",
"keywords": [
"不存在",
"单key",
"多key",
"持续高压",
"瞬时尖峰",
"集体失效"
],
"scoring_rubric": "准确定义穿透并答对应对策 33 分;准确定义击穿并答对 33 分;准确定义雪崩并答对 34 分。",
"explanation": "记忆口诀:穿透查「空」,击穿防「单点失效」,雪崩防「批量失效/宕机」。三者最终都要保护 DB。",
"source": null,
"related": []
},
{
"id": "sa-008",
"type": "short_answer",
"difficulty": 2,
"tags": [
"缓存穿透",
"布隆过滤器"
],
"question": "既然布隆过滤器有误判可能,为什么仍可用于防缓存穿透?",
"answer": "因为布隆过滤器只存在假阳性(判断存在可能不存在),而不存在假阴性(判断不存在则一定不存在)。防穿透时我们最需要的是把「不存在的 key」挡在 DB 之前,对这类 key 布隆过滤器会给出确定的「不存在」结论从而直接拦截,不会漏放;即使有极少数误判为「存在」的空 key 放行,也会被后续空值缓存等兜底。误判只可能多放、不会漏放的不存在请求,所以可用。",
"keywords": [
"无假阴性",
"不存在一定拦截",
"假阳性可接受",
"空值兜底"
],
"scoring_rubric": "说明无假阴性(不存在→一定不存在)得 40 分;解释用它拦截不存在的 key 得 30 分;解释误判为正存在可被兜底得 30 分。",
"explanation": "关键是方向:布隆过滤器对「不存在」的结论是 100% 可靠,而防穿透恰好需要拦截人多不存在数据不被穿透。",
"source": null,
"related": []
},
{
"id": "sa-009",
"type": "short_answer",
"difficulty": 4,
"tags": [
"缓存空对象",
"缓存穿透"
],
"question": "缓存空对象方案解决了穿透,但可能带来什么新问题?如何缓解?",
"answer": "新问题:大量【不同的不存在 key】都会被缓存为空对象,而这些键永远占着内存且无法被正常命中,长期积累会挤满缓存内存、浪费空间。缓解:1) 给空对象设置极短 TTL(如 5~10 分钟)让其及时失效;2) 结合布隆过滤器在入口先拦截明显不存在的 key,减少写入空缓存的数量;3) 做好内存淘汰与监控。",
"keywords": [
"占满内存",
"多空key",
"极短TTL",
"布隆过滤器拦截",
"内存告避免"
],
"scoring_rubric": "准确指出内存被大量空 key 占用得 40 分;给出短 TTL 缓解得 30 分;给出布隆过滤器/内存控制补充方案得 30 分。",
"explanation": "空值缓存是「空间换时间」,代价是空间,所以必须用短 TTL + 前置拦截控制空 key 数量。",
"source": null,
"related": []
},
{
"id": "sa-010",
"type": "short_answer",
"difficulty": 5,
"tags": [
"多级缓存",
"Redis高可用",
"缓存雪崩"
],
"question": "结合多级缓存思路,说明在缓存雪崩/宕机风险下如何设计一套层层兜底的缓存架构。",
"answer": "可设计三层:最前一层用本地进程内缓存(Caffeine 类),承接热读取吞吐;中间层用 Redis 分布式缓存,主从+哨兵/集群保证高可用,避免宕机;最后层是数据库。访问顺序本地→Redis→DB,每层 miss 才下沉到下一层。配合机制:TTL 加随机打散集体过期;对 DB 再加限流、熔断、降级(如降级返回旧值或默认数据)。即使 Redis 瞬时不可用,本地缓存仍能扛住大部分读请求,DB 请求被限流保护,避免整体雪崩。",
"keywords": [
"本地缓存",
"Redis",
"数据库",
"层层兜底",
"高可用",
"限流熔断降级"
],
"scoring_rubric": "给出本地+分布式+DB 分层架构得 40 分;说明访问顺序与每层兜底得 30 分;结合 TTL随机、限流降级、高可用写出 30 分。",
"explanation": "多级缓存的本质是「冗余缓存层 + 兜底限流」,即使上层失效也由下层承接或降级,从架构上消除单点雪崩。",
"source": null,
"related": []
}
]
}