Files
examination/topics/thumbup/two-level-cache/single_choice.json
T

176 lines
8.3 KiB
JSON
Raw 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.
{
"topic": "two-level-cache",
"type": "single_choice",
"schema_version": "1.0.0",
"generated": "2026-09-09T21:42:00+08:00",
"questions": [
{
"id": "sc-001",
"type": "single_choice",
"difficulty": 2,
"tags": [
"two-level-cache",
"CacheManager",
"architecture"
],
"question": "在 Caffeine + Redis 二级缓存架构中,CacheManager.get() 的第一步查询目标是什么?",
"options": {
"A": "直接查询 Redis(L2 缓存)",
"B": "查询 Caffeine 本地缓存(L1 缓存)",
"C": "查询空值缓存(null cache)",
"D": "加锁后执行 double-check 查询 Redis"
},
"answer": "B",
"explanation": "CacheManager.get() 的完整流程为:查 L1(Caffeine)→ 查空值缓存 → 加锁 double-check → 查 Redis → HeavyKeeper 记录 → 热 Key 提升 L1。第一步始终是查询本地 Caffeine 缓存,命中则直接返回,未命中才进入后续流程。",
"source": null,
"related": []
},
{
"id": "sc-002",
"type": "single_choice",
"difficulty": 3,
"tags": [
"two-level-cache",
"null-cache",
"lock"
],
"question": "CacheManager.get() 流程中,空值缓存(null cache)的作用是什么?",
"options": {
"A": "缓存 Redis 中存储的空值对象,防止穿透",
"B": "记录已查询过但 Redis 中不存在的 Key,避免重复穿透到 Redis",
"C": "作为 L1 和 L2 之间的一致性校验缓存",
"D": "存储 HeavyKeeper 探测到的热 Key 列表"
},
"answer": "B",
"explanation": "空值缓存用于记录已确认在 Redis 中不存在的 Key,后续相同 Key 的查询命中空值缓存后直接返回,不再穿透到 Redis,有效防止缓存穿透。注意这里缓存的不是 Redis 中的空值对象,而是对 Key 不存在这一事实的标记。",
"source": null,
"related": []
},
{
"id": "sc-003",
"type": "single_choice",
"difficulty": 2,
"tags": [
"Caffeine",
"W-TinyLFU",
"eviction"
],
"question": "Caffeine 使用的 W-TinyLFU 淘汰策略中,「W」代表什么含义?",
"options": {
"A": "Write-through(写穿透)",
"B": "Window(窗口)",
"C": "Weighted(加权)",
"D": "Waiting(等待)"
},
"answer": "B",
"explanation": "W-TinyLFU 中的 W 代表 Window,即窗口缓存。W-TinyLFU 将缓存分为 Window(窗口)和 Main(主)两个区域。新数据先进入 Window 区,通过准入策略(admission filter)决定是否提升到 Main 区。Main 区内部再分为 TinyLFU 的 probation 和 protected 两个子区。这种分层设计兼顾了频率统计精度和内存效率。",
"source": null,
"related": []
},
{
"id": "sc-004",
"type": "single_choice",
"difficulty": 3,
"tags": [
"HeavyKeeper",
"hot-key",
"detection"
],
"question": "HeavyKeeper 热 Key 探测算法的 add() 方法在遇到桶冲突时,执行的是什么操作?",
"options": {
"A": "直接覆盖桶中原有的 fingerprint 和 count",
"B": "累加已有 fingerprint 对应桶的 count",
"C": "以概率 decay 对已有 count 进行衰减,若衰减后 count < minCount 则替换为新 fingerprint,否则保留原 fingerprint",
"D": "将冲突 Key 放入溢出队列,异步处理"
},
"answer": "C",
"explanation": "HeavyKeeper 的 add() 有三个分支:1) 桶为空时直接写入 fingerprint 和 count=1;2) fingerprint 匹配时直接 count++;3) 桶冲突时以概率 decay 对已有 count 衰减,衰减后若 count < minCount(阈值为 10)则替换为当前 fingerprint 和 count=1,否则保留原 fingerprint。这一设计利用概率衰减自然淘汰冷 Key,保留热 Key。",
"source": null,
"related": []
},
{
"id": "sc-005",
"type": "single_choice",
"difficulty": 4,
"tags": [
"HeavyKeeper",
"fading",
"decay"
],
"question": "HeavyKeeper 的 fading() 方法每 20 秒执行一次,其核心操作是什么?",
"options": {
"A": "清空所有桶的 count,重新统计",
"B": "将所有桶的 count 右移一位(等效于除以 2,减半衰减)",
"C": "只对 count > minCount 的桶执行衰减",
"D": "删除所有 fingerprint 为空的桶"
},
"answer": "B",
"explanation": "fading() 每 20 秒触发一次,对 HeavyKeeper 的所有 bucket 的 count 执行右移一位操作,即 count >>= 1,等效于将所有计数值减半。这实现了时间维度的指数衰减,使 HeavyKeeper 能够反映 Key 的近期热度而非累积热度。配合 add() 中的冲突衰减(decay=0.92),形成双重衰减机制。",
"source": null,
"related": []
},
{
"id": "sc-006",
"type": "single_choice",
"difficulty": 3,
"tags": [
"two-level-cache",
"write-consistency",
"putIfPresent"
],
"question": "在二级缓存架构中,更新数据时使用 putIfPresent 写入 Redis 的主要目的是什么?",
"options": {
"A": "提高写入性能,减少 Redis 操作次数",
"B": "保证只有在 Key 已存在时才更新,防止误覆盖其他线程新写入的数据",
"C": "确保 Redis 和 Caffeine 的写入顺序一致",
"D": "自动触发 L1 缓存的失效通知"
},
"answer": "B",
"explanation": "putIfPresent 是 Redis 的条件写入命令,只有当 Key 已存在时才会执行更新操作并返回 OK,否则返回 nil。在二级缓存更新场景中,这可以防止并发更新时的误覆盖问题——如果另一个线程已经删除并重建了该 Key,当前线程的写入不会覆盖新数据。这是一种乐观锁思路的写一致性保障。",
"source": null,
"related": []
},
{
"id": "sc-007",
"type": "single_choice",
"difficulty": 4,
"tags": [
"two-level-cache",
"inconsistency",
"L1-L2"
],
"question": "在 Caffeine + Redis 二级缓存中,以下哪种场景最容易导致 L1 和 L2 之间的数据不一致?",
"options": {
"A": "L1 缓存容量设置过小导致频繁淘汰",
"B": "多个应用实例同时更新 Redis 中的同一 Key,但各实例的 Caffeine 失效通知未及时送达",
"C": "Caffeine 的 expireAfterWrite 设置过长",
"D": "Redis 采用主从架构导致读写延迟"
},
"answer": "B",
"explanation": "多实例场景下,当一个实例更新了 Redis 数据后,需要通过消息广播(如 Redis Pub/Sub、RocketMQ 等)通知其他实例失效本地 Caffeine 缓存。如果通知延迟或丢失,其他实例的 Caffeine 中仍持有旧数据,而 Redis 已经是新数据,造成 L1/L2 不一致。这是分布式二级缓存最经典的一致性问题。A 和 C 影响的是缓存命中率,D 影响的是主从一致性而非 L1/L2 一致性。",
"source": null,
"related": []
},
{
"id": "sc-008",
"type": "single_choice",
"difficulty": 5,
"tags": [
"HeavyKeeper",
"parameters",
"configuration"
],
"question": "HeavyKeeper 当前配置为 k=100, width=100000, depth=5, decay=0.92, minCount=10。当一个新 Key 被 add() 时,该 Key 的 fingerprint 需要经过几个哈希函数映射到几个桶中进行检测?",
"options": {
"A": "1 个(随机选一个桶)",
"B": "5 个(depth 个桶)",
"C": "100 个(k 个桶)",
"D": "100000 个(width 个桶)"
},
"answer": "B",
"explanation": "HeavyKeeper 中,depth 决定了每个 Key 需要检测的桶数量。对于每个被 add 的 Key,先计算其 fingerprint,然后通过 depth(此处为 5)个不同的哈希函数映射到 width 个桶中的 5 个位置。在这 5 个桶中,按 add() 的三分支逻辑处理。depth 越大,对热 Key 的识别精度越高,但内存开销也相应增加。k 是返回的 Top-K 热 Key 数量,width 是桶数组大小,decay 和 minCount 控制衰减和淘汰。",
"source": null,
"related": []
}
]
}