JJava 知识库
JAVA INTERVIEW

高频面试题

架构高级约 3 分钟

设计一个高并发商品详情服务,缓存、数据库和变更消息之间的一致性怎么保证?

参考回答约 3 分钟 · 口语表达
先说结论

我会先定目标和容量,再拆核心链路:常见方案是 Cache Aside:读请求先查缓存,未命中再查数据库并回填;写请求先更新数据库,再删除缓存。它通常保证最终一致性,而不是严格强一致。

01

我不会直接画架构图,会先确认目标、规模和一致性要求。从 Cache Aside、延迟双删和消息通知理解一致性取舍。常见方案是 Cache Aside:读请求先查缓存,未命中再查数据库并回填;写请求先更新数据库,再删除缓存。它通常保证最终一致性,而不是严格强一致。

02

容量有了以后,再把入口、核心处理和数据落点串起来。沿着「业务写入事实库 → 事务提交形成版本 → 删除/更新缓存 → 并发读回源与回填 → 可靠重试和对账收敛」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 业务写入事实库 先明确数据库或事件日志是事实源,缓存只能从可恢复事实派生。 事务提交形成版本 事务提交点给数据一个确定版本,失败时不能提前让缓存暴露未提交状态。 删除/更新缓存 Cache Aside 常在提交后删除缓存,删除失败必须进入可观测重试而不是忽略。 数据库 commit/binlog:事实版本。 缓存 value.sourceversion:旧值识别。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。

03

关键参数要从峰值流量和资源上限反推。常见方案是 Cache Aside:读请求先查缓存,未命中再查数据库并回填;写请求先更新数据库,再删除缓存。它通常保证最终一致性,而不是严格强一致。

04

正常链路之外,还要设计失败补偿和可验证的恢复流程。写服务更新数据库后删除一个缓存 Key,但聚合服务维护另一份派生缓存且未收到事件。通过统一版本事件、所有缓存记录 sourceversion,并用对账扫描补发后,跨服务缓存才收敛。 缓存故障时无限回源压垮数据库:源版本与缓存版本差:提交后删并可靠重试。 旧事件晚到覆盖新版本:删除/更新重试积压:回填带版本条件。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「缓存/源版本差」为主基线,记录值应满足「记录活动/稳态基线」;同时保存 源版本与缓存版本差、删除/更新重试积压,使后续变化能够回到同一时间轴比较。

05

最后再讲扩容、成本和方案边界。方案:更适合的场景:主要收益:代价与边界。 Cache Aside:通用读多写少:简单、按需加载:删除失败与并发回填窗口。 写穿/写后同步:写后读必须快速一致:缓存及时更新:双写失败和顺序复杂。 CDC/事件驱动:多缓存或多写入口:以提交日志可靠收敛:延迟、乱序和运维成本。 选型至少带上 峰值流量、数据规模、依赖容量、一致性目标和故障预算,并用上面的量化基线验证;未知数据应明确为待测假设。

方案主链路从目标到落地
01业务写入事实库
02事务提交形成版本
03删除/更新缓存
04并发读回源与回填
05可靠重试和对账收敛