1238 字
约 4 分钟
3
通用架构笔记 · 缓存失效与"改了就更新"的实现
通用架构笔记 · 缓存失效与"改了就更新"的实现
用途:沉淀一条可复用的架构决策——系统用缓存提升性能时,如何正确解决"用户改了输入,结果却不更新"的经典问题。 可迁移到任何有缓存/AI 生成/编辑器的项目。
一、问题定义(先在头脑里框定问题)
症状:用户改了输入(如调整切图/选区),AI 生成的结果或预览仍是旧的。
本质原因:缓存(如 html-preview-cache)把上一次的结果存了下来,但"输入变化 → 缓存失效"这条联动没接全,导致命中了脏数据。这不是崩溃,是静默地显示旧数据——最坑的一类 bug,因为它不报错,极难察觉。
性能的诱惑:AI 生成又慢又贵,不可能每次改动都重调;所以缓存是刚需。真正困难的是**"什么时候该作废"**。
二、方案谱系(由简单到彻底,按需选)
| # | 方案 | 做法 | 代价 | 适用 |
|---|---|---|---|---|
| ① | 手动失效 | 「重新识别」按钮 + 重启后端 | 体验差,靠人,容易漏 | 兜底 / 紧急 |
| ② | 内容指纹 (digest) | 把输入算成 hash 放进缓存 key,输入变→key 变→自动 miss | 极低,一行 hash | ★ 大多数场景首选 |
| ③ | 依赖追踪 (dep graph) | 显式记录"结果依赖哪些输入",dep 变→标 stale | 中 | 依赖复杂、多共享 |
| ④ | 乐观失效 + 后台重建 | 改动即作废,后台悄悄重算新稿 | 中高,后台任务 | 高频交互编辑器 |
| ⑤ | 结果级缓存→请求级去重 | 缓存"请求本身"而非结果,相同输入→幂等复用 | 中高 | 想省模型调用费 |
三、推荐落地:方案② 内容指纹(digest)
核心思路:把"这次任务的输入"算成一个哈希指纹,作为缓存 key 的一部分。
cacheKey = "preview:" + sha256( 原始截图 + 用户改动后的选区/参数 + 模型/config 版本 )
- 用户改了切图 → 指纹变 → key 变 → 自动 cache miss,重新生成。
- 用户没改 → 指纹不变 → 命中旧缓存,又快又不乱调 AI。
关键点——指纹里的"输入"要包含什么:
- 源数据(截图/文档)
- 用户所有可改变结果的操作参数(选区、宽高、阈值)
- 会改变输出的"版本类"信息(用哪个模型、prompt 版本),否则换模型也不会刷新
可迁移配方句:
When 缓存的产物取决于一组输入, use 把输入算成内容指纹放进缓存 key, because 输入一变 key 就变,缓存自动失效,无需手动清。
四、更完整:方案②+④ 组合(推荐用于编辑器类项目)
- 改动写入即作废:用户每一步操作都让对应缓存失效(invalidate-on-write),界面立刻反映"这里要重算了"。
- 后台重建:不阻塞用户,后台任务悄悄算出新稿,回来覆盖预览;前端可给"识别中…"轻提示。
- 请求去重(可叠加⑤):对完全相同的请求幂等,省模型调用费。
五、工程判断 / 反模式提醒
- ✅ 正确心态:当系统用缓存换取性能时,必须把"缓存失效"当作一等需求来设计,而不是事后再打补丁。
- ❌ 反模式:靠"重启服务/手动刷新"兜底 —— 这暴露了失效逻辑没接对;正确做法是让"输入变化→自动失效对应缓存"链路闭环。
- ❌ 过度设计:若依赖不复杂,不要上来就上"依赖图/事件总线",优先用指纹,简单且够用。
六、选型速查(给别人/自己以后快速决策)
要解决的问题:缓存不更新 / 改输入没反应
├─ 输入单一、依赖不复杂 → 方案② 内容指纹(默认首选)
├─ 交互高频、要即时反馈 → 方案②+④ 乐观失效+后台重建
├─ 省模型调用费 → 叠加 方案⑤ 请求去重
└─ 结果被多处、复杂地复用 → 方案③ 依赖追踪
(方案① 手动+重启 只留作紧急兜底)
七、相关笔记/位置
- 本项目的具体缓存实现位置:
html-preview-cache(可到src/内 grep,查看失效逻辑)。 - 关联通用主题:
双线程消息契约、AI 输出容错(双解析)、大脑-仓库分离。
通用架构笔记 · 缓存失效与"改了就更新"的实现
http://clxhxhhr.top/posts/548/ 评论
0 条
还没有评论,先写一条吧。