{ "topic": "cache-read-write-strategy", "type": "single_choice", "schema_version": "1.0.0", "generated": "09-2026-09-05T00:00:00+08:00", "questions": [ { "id": "sc-001", "type": "single_choice", "difficulty": 1, "tags": [ "Cache-Aside", "写策略" ], "question": "Cache-Aside(旁路缓存)策略中,写操作的标准流程是?", "options": { "A": "先删除缓存,再写数据库", "B": "先写数据库,再失效(删除)缓存", "C": "只写缓存,不同步数据库", "D": "先写缓存,再同步写数据库" }, "answer": "B", "explanation": "Cache-Aside 的主流写法是「先写 DB(真相源),再删除缓存」,下次读请求 miss 后重新查库回填。选项 A 是容易引入不一致的反模式,C 会丢失数据,D 是 Write-Through 的做法。", "source": null, "related": [] }, { "id": "sc-002", "type": "single_choice", "difficulty": 1, "tags": [ "Cache-Aside", "CacheLoader", "Read-Through" ], "question": "Cache-Aside 策略中,读请求发生缓存 miss 时的正确处理是?", "options": { "A": "直接返回空,不访问数据库", "B": "由应用自行查询数据库,并把结果回填(写入)缓存", "C": "只把数据返回给调用方,不回填缓存", "D": "删除该 key 对应的缓存后再返回" }, "answer": "B", "explanation": "Cache-Aside 的读路径:先查缓存,miss 后由应用查数据库,拿到结果后回填缓存并返回。这样后续相同 key 的读请求都能命中缓存,避免每次打库。C 不回填会导致缓存失去意义。", "source": null, "related": [] }, { "id": "sc-003", "type": "single_choice", "difficulty": 3, "tags": [ "Write-Through", "Write-Behind", "一致性" ], "question": "关于 Write-Through(写穿透)与 Write-Behind(异步回写)的区别,以下说法正确的是?", "options": { "A": "Write-Through 先写缓存后异步批量刷库,吞吐高但可能丢数据", "B": "Write-Behind 写缓存即同步写数据库,强一致但慢", "C": "Write-Through 是「先写缓存再同步写数据库」,一致性较强但写性能较低", "D": "两种策略在写路径上完全等价" }, "answer": "C", "explanation": "Write-Through 写缓存即同步写 DB,一致性较强但慢;Write-Behind 写进缓存就先返回,DB 由后台异步批量刷写,吞吐高但有丢失/乱序风险。A、B 把两者对调混淆了,D 错误。", "source": null, "related": [] }, { "id": "sc-004", "type": "single_choice", "difficulty": 3, "tags": [ "Write-Behind", "Write-Back", "数据安全" ], "question": "对于交易、余额、库存这类强一致+不可丢失的数据,下列哪种缓存策略是绝不可取的?", "options": { "A": "Cache-Aside(先写库再删缓存)", "B": "Write-Through(写库即同步写库)", "C": "Write-Behind(异步批量落库)", "D": "Read-Through(读缓存 miss 自动查库回填)" }, "answer": "C", "explanation": "Write-Behind/Write-Back 采用异步批量刷库,存在缓存宕机导致写入丢失、乱序、外部读到旧值等风险,只适合最终一致+可容忍丢失的数据(计数、会话、中间态),绝不可用于交易/余额/库存这类数据。", "source": null, "related": [] }, { "id": "sc-005", "type": "single_choice", "difficulty": 3, "tags": [ "多级缓存", "L1", "L2" ], "question": "经典的多级缓存结构中,本地进程内缓存(如 Caffeine)相比共享 Redis 缓存的优势是?", "options": { "A": "可以跨节点共享数据,重启后不丢失", "B": "零网络开销,读取延迟最低,能扛住超高 QPS", "C": "支持持久化和主从集群", "D": "数据全自动与数据库保持一致" }, "answer": "B", "explanation": "L1 本地进程内缓存(Caffeine)读不走网络,延迟最低,适合热点 key 的超高 QPS;代价是单机副本、重启即失、跨节点不一致。A、C 是 Redis(L2 共享)的优势,D 在所有缓存策略下都不成立。", "source": null, "related": [] }, { "id": "sc-006", "type": "single_choice", "difficulty": 4, "tags": [ "多级缓存", "一致性" ], "question": "多级缓存架构中最需要付出代价、也是最难解决的是?", "options": { "A": "缓存容量不足", "B": "各级缓存的命中率", "C": "同一数据在多个缓存层/多个节点间的一致性", "D": "本地缓存占用内存过大" }, "answer": "C", "explanation": "多级缓存里同一数据会同时存在于 L1 本地、L2 Redis、多个节点上,更新时任一副本残留旧值都会造成不一致。所以一致性是多级缓存最大的代价,需要通过广播失效、短 TTL、回调等机制缓解。", "source": null, "related": [] }, { "id": "sc-007", "type": "single_choice", "difficulty": 4, "tags": [ "Caffeine", "TinyLFU", "淘汰算法" ], "question": "Caffeine 本地缓存的默认淘汰/近似最合适算法是 TinyLFU,它与 LRU 相比的主要改进是?", "options": { "A": "淘汰顺序完全随机,降低计算开销", "B": "只按访问时间决定淘汰,实现更简单", "C": "基于访问频率(并辅以最近访问时间/衰减),能保留短期突发但长期高频的 key,避免 LRU 被顺序扫描冲垮", "D": "淘汰后立即删除数据库副本,保证一致性" }, "answer": "C", "explanation": "TinyLFU 通过维护计数器记录 key 的访问频率,并结合时间窗口衰减与最近访问,避免 LRU 被「批量顺序扫描」一次性清空高频 key,淘汰决策更聪明。A、B 描述错误,D 属其他机制。", "source": null, "related": [] }, { "id": "sc-008", "type": "single_choice", "difficulty": 4, "tags": [ "版本号回填", "竞态" ], "question": "Cache-Aside「先写库再删缓存」的竞态窗口,最恶劣的后果是?", "options": { "A": "某个读请求多一次数据库查询", "B": "删缓存操作偶尔失败", "C": "读线程在删缓存之后把库里的旧值回填进缓存,导致旧值长期残留缓存", "D": "写请求直接丢失数据" }, "answer": "C", "explanation": "最严重的是「来源2」:读线程在写线程删缓存的时刻库仍是旧值,读操作回填,且可能发生在删除之后,于是旧值被写进缓存且无法用「删缓存」治愈。它会导致后续所有读请求都命中旧值,直到 TTL 到期或再次写。", "source": null, "related": [] }, { "id": "sc-009", "type": "single_choice", "difficulty": 4, "tags": [ "双删", "版本号回填", "竞态" ], "question": "下列哪种方案可以从「在缓存中残留旧值」这一类竞态问题,适合强一致且不易辩的 key?", "options": { "A": "删除缓存前先 sleep 100ms", "B": "写成功后连删两次缓存(双删)", "C": "缓存中带版本号,回填前比较,旧版本直接丢弃", "D": "写入缓存后就放弃数据库" }, "answer": "C", "explanation": "版本号回填会在回填前比较缓存中的版本号与要写入的一致性,旧版本直接丢弃,因此从根源上避免「旧值被回填」——这也是“版本号是最彻底的方案”的原因。双删只能提高第二次删除覆盖旧可能,不能保证绝对干净。", "source": null, "related": [] }, { "id": "sc-010", "type": "single_choice", "difficulty": 4, "tags": [ "Write-Behind", "乱序", "丢失" ], "question": "Write-Behind(异步回写)存在多种风险,下列哪一项是 Write-Behind 独有、Cache-Aside 也会出现但策略不同?", "options": { "A": "缓存 miss 后需要回填", "B": "异步落库可能存在「缓存宕机且落库前数据丢失」", "C": "删缓存后读线程仍读库", "D": "本地缓存无法节点间共享" }, "answer": "B", "explanation": "「先写缓存即返回成功、DB 后台异步刷」是 Write-Behind 的核心,因此若缓存进程在落库前宕机,这些更新就丢了。这是 Write-Behind 独有的风险,也再次印证它只适合可丢失的最终一致数据。", "source": null, "related": [] } ] }