vault backup: 2026-08-09 19:06:40
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
---
|
||||
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 热点探测算法]]
|
||||
Reference in New Issue
Block a user