Files
autumn-recruitment/03.Redis/strategies/多级缓存架构设计_test.md

151 lines
7.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [test/review, redis, multi-level-cache, caffeine, cache-consistency, avalanche]
create time: 2026-08-09 12:00
---
# 多级缓存架构设计_测试题
## 概述
本测试覆盖 L1/Caffeine 本地缓存、L2/Redis 远程缓存和 L3/MySQL 持久层组成的三级缓存架构。共 10 道题:6 道选择题、3 道填空题、1 道简答题,考察一致性策略、雪崩防护和 capacity planning 的计算能力。
---
## 一、选择题(6道,由浅入深)
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
### Q1(基础)— 考察定义层面
在三级缓存架构(Caffeine → Redis → MySQL)中,L1 缓存最主要的局限性是什么?
A. 读写延迟高于 L2 缓存
B. 数据无法在多实例间同步,每个实例有自己的缓存视图
C. 不支持任何淘汰算法
D. 只能通过配置文件一次性设置,运行期不可调整
### Q2(基础)— 考察行为判断
关于二级缓存的一致性更新策略,以下哪种做法**最不推荐**?
A. 先更新 DB 再删除缓存
B. 先删除缓存再更新 DB
C. 先删缓存再写 DB——会导致写操作完成后、DB 写入前的窗口期内读请求拿到陈旧缓存
D. 使用消息队列广播各实例清除自己的 L1 缓存
### Q3(进阶)— 考察核心原理
在多级缓存中,使用 singleflight 来防止"缓存击穿"的核心机制是:
A. 在 L1 层互斥锁保证只有一个线程能读 L2
B. 对同一个 key 的并发查询只允许一个线程执行底层查找函数,其余线程共享结果
C. 在 L2 Redis 层设置分布式锁防止同时写入
D. 自动将所有并发请求合并为一次批量查询
### Q4(进阶)— 考察对比辨析
以下哪组缓存失效传播方案各有不同的适用场景?
A. 短 TTL + 异步刷新适用于实时性要求极高的场景;MQ 广播适用于大规模部署
B. MQ 广播实时性好但增加系统复杂度;Canal 监听 binlog 解耦彻底适合大规模部署
C. 定时扫描比对成本最低且最可靠;Canal 监听只适用于 MySQL
D. 三种方案性能完全相同,区别仅在于实现语言
### Q5(深入)— 考察场景推理
某电商商品详情页使用多级缓存。单机 QPS 约为 10000,L1 hit rate = 90%,L2 hit rate(相对 L1 miss)= 80%。请问最终打到外部 Redis 的请求大约是多少 QPS?
A. 约 8000 QPS
B. 约 2000 QPS
C. 约 200 QPS
D. 约 20 QPS
### Q6(深入)— 考察源码级别细节
在 singleflight 实现的缓存获取流程中,如果 L1 miss 且 L2 也 miss,singleflight 的作用范围是:
A. 仅保护对 L1 的写入操作
B. 保护从 DB 查询并回填整个缓存链路的完整过程
C. 仅保护对 L2 Redis 的 SET 操作
D. 保护所有客户端的 GET 请求
---
## 二、填空题(3道)
### F1 — 填空
在 TTL 随机化防雪崩的代码中,`baseMinutes=10, jitterPercent=30` 时,实际 TTL 落在 ______ 分钟到 ______ 分钟之间均匀分布。
> **提示**: actualMinutes = baseMinutes - jitter + rand.Intn(2*jitter),其中 jitter = baseMinutes * jitterPercent / 100。
### F2 — 填空
空值缓存用于防护缓存穿透:查询结果为空时也缓存一个特殊标记(如 nil),设极短 TTL,通常范围为 ______ 秒到 2 分钟。
> **提示**: 回顾文档中的具体数值范围。
### F3 — 填空
当多个应用实例同时修改同一个 key 导致缓存缺失时,会出现两个问题:重复重建和 ______ ,即 L1 缓存不知道 L2 已经被删除而继续提供旧数据。
> **提示**: 这是缓存一致性问题的另一个典型表现。
---
## 三、简答题(1道)
### S1
一个新闻聚合 App 有以下特点:
- 热门文章更新频率极低(小时级),但单次访问量大(万级 QPS)
- 文章发布后需要尽快让所有用户看到最新版本
- App 有多个服务实例分布在不同的可用区
请设计一个多级缓存方案,回答以下问题:
1. L1、L2、L3 分别使用什么技术?为什么?
2. 如何保证文章更新后各实例的 L1 缓存能尽快刷新?
3. 如何防止大量缓存同时过期导致的雪崩?
> **答题框架提示**:
> 1. 各层的技术选型依据
> 2. 失效传播的具体方案(MQ/binlog/短TTL组合)
> 3. TTL 随机化的具体参数建议
---
## 参考答案与解析
### 选择题答案
| 题号 | 正确答案 | 解析 |
|------|---------|------|
| Q1 | B | L1 Caffeine 基于 JVM 堆内内存,多实例间数据无法同步,每个实例有自己的缓存视图。A 错——L1 微秒级远低于 L2 毫秒级;C 错——支持 LRU、LFU、Window LFU 等;D 错——运行期可动态调整。 |
| Q2 | B | 先删缓存再写 DB 是最危险的:写操作完成后、DB 写入前的窗口期内,读请求会拿到陈旧缓存(或者查到 DB 旧数据重写回缓存)。推荐先更新 DB 再删缓存,极端竞态概率更低。 |
| Q3 | B | singleflight 的核心机制:对于同一个 key 的并发查询,只有一个 goroutine 执行 fn() 函数去查 DB,其他 goroutine 等待该结果并直接返回。这避免了大量并发请求同时穿透到数据库。 |
| Q4 | B | A 反了——MQ 广播实时性好但不是最适合高实时场景;短 TTL 更适合容忍短暂不一致的场景。C 错误——定时扫描延迟高且成本高不是最优方案。D 错误——三种方案性能和架构差异明显。 |
| Q5 | C | 计算方法:10000 × (1 - 0.9) × (1 - 0.8) = 10000 × 0.1 × 0.2 = 200 QPS。L1 拦截了 9000 QPS,剩余 1000 进入 L2,L2 拦截 800 条,最终 200 QPS 到达 Redis 甚至下游。这是评估集群规模的核心指标。 |
| Q6 | B | singleflight.Group.Do(key, fn) 的作用范围是从 DB 查询到将结果写回 L1 和 L2 的完整链路。它确保同一时刻对同一 key 只有一个 DB 查询在执行,其余请求共享该结果。 |
### 填空题答案
| 题号 | 答案 | 解析 |
|------|------|------|
| F1 | 7 | 13 | jitter = 10 * 30 / 100 = 3;actualMinutes = 10 - 3 + rand.Intn(2*3) = 7 ~ 13,均匀分布。这样可以避免大量 key 在同一时刻过期。 |
| F2 | 30 | 空值缓存的 TTL 一般为 30s~2min。时间太短会让穿透攻击持续有效,太长则浪费存储和增加清理负担。适用于不存在的数据比例较低的场景。 |
| F3 | 脏数据残留 | L1 缓存不知道 L2 已经被删除而继续提供旧数据,这就是"脏数据残留"问题。解决策略包括短 TTL 兜底、MQ 广播或 Canal binlog 监听等方式。 |
### 简答题参考答案
S1:**参考答案要点**:
1. L1 用 Caffeine(JVM 堆内内存,微秒级延迟),适合存最热点的几篇热门文章;L2 用 Redis(集中式共享缓存,毫秒级),存全量活跃文章;L3 用 MySQL(最终数据来源)。理由:读取频率极高但更新低频,L1 拦截 90%+ 流量。
2. 失效传播采用 Canal + binlog 监听方案:文章发布后解析 MySQL binlog,自动推送 invalidate 事件给各实例清除对应文章的 L1 缓存。配合短 TTL(如 1 小时)加后台预刷作为兜底。
3. TTL 随机化:在基础 TTL(如 10 分钟)上加减 30% 的随机偏移,使过期时间均匀分布在 7~13 分钟,避免集中过期。
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖三层选型、失效传播方案和雪崩防护三个维度。
## 关联笔记
- [[03.Redis/core/Redis 五大核心数据结构]]
- [[03.Redis/strategies/缓存穿透击穿雪崩解决方案]]
- [[03.Redis/strategies/旁路缓存与读写策略]]
- [[03.Redis/strategies/HeavyKeeper 热点探测算法]]