返回探索
指南VibeFix 编辑部更新于 2026年10月8日

缓存是 vibe 项目 ROI 最高的性能手段,也是 bug 最多的地方:一份从浏览器到 AI 结果的完整实战

每个 vibe 项目迟早会遇到同一个时刻:列表页一打开就要查十几次库,并发稍高数据库就被打满。这篇实战从缓存的三问心智模型讲起,逐层拆解 HTTP 缓存头、Next.js 数据缓存、Redis 应用缓存与 AI 结果缓存(语义缓存/prompt 缓存),给出缓存键设计、穿透击穿雪崩的三件套解法和失效策略,最后附一份上线检查清单。

数据中心机房里的服务器与网线,象征 vibe 项目的缓存架构与性能优化

每个 vibe 项目都会遇到同一个时刻:开发时列表页秒开,上线第一天用户稍多,数据库就被打满,页面转圈 5 秒。AI 帮你 10 分钟搭出的 CRUD,藏着一个它不会主动告诉你的真相——它默认没有任何缓存。

缓存是 vibe 项目里 ROI 最高的性能手段:不改架构、不加机器,几行配置就能把响应从秒级压到毫秒级。但它也是 bug 最多的地方:「为什么用户改了头像还是旧的」「为什么订单状态不同步了」——十个有九个是缓存失效没做对。这篇实战的目标是:给你一套从浏览器到 AI 结果的完整缓存地图,以及每层的「什么时候用、怎么配、坑在哪」。

先建立心智模型:缓存三问

动手之前,先对任何要缓存的东西连问三句:

1. 这东西变不变? 用户头像(低频变)、商品列表(中频变)、实时股价(高频变)——变化频率决定缓存策略。不变的东西大胆缓存,常变的东西谨慎缓存。

2. 缓存多久能接受? 不是问「最长能缓存多久」,而是问「用户能忍受多久的旧数据」。博客文章缓存 1 小时没人察觉;订单状态缓存 1 分钟都可能出客诉。TTL(过期时间)是业务决策,不是技术参数。

3. 失效时怎么办? 缓存没命中、缓存挂了,系统能不能优雅降级回源站?如果答案是「不能」,那你做的不是缓存,是单点故障。

记住缓存的层级(请求从上往下走,越上越便宜):浏览器 → CDN → 应用/页面缓存 → 数据库查询缓存。优化永远从上往下做——浏览器缓存 0 成本,Redis 缓存要运维,别倒过来。

第一层:HTTP 缓存头,别让浏览器重复要

最被低估的一层,成本为零。AI 生成的代码经常完全不设缓存头,等于告诉浏览器「每次都重新问我要」。

静态资源(JS/CSS/图片,文件名带 hash 的):直接给一年:

Cache-Control: public, max-age=31536000, immutable

文件名带 hash 意味着内容变了文件名就变,immutable 告诉浏览器「这辈子都不用再问」,省掉一次条件请求。

半静态内容(文章页、商品详情):

Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=60

这行配置的意思是:浏览器缓存 60 秒;CDN 缓存 300 秒;过期后 60 秒内先返回旧内容、后台悄悄更新——stale-while-revalidate 是 vibe 项目的神器,用户永远感觉快,数据最多旧 1 分钟。

需要强一致的接口(订单状态、余额):

Cache-Control: no-store

别用 no-cache 充数——no-cache 的意思是「可以存,但每次用前先问我」,no-store 才是「别存」。AI 生成的代码经常把两者搞混,支付相关接口务必用 no-store。

第二层:页面与数据缓存(Next.js 视角)

如果你用 Next.js(vibe 项目最常见),AI 默认生成的全是动态渲染——每个请求都从头查库。这是最常见的性能坑,也是最好修的。

读多写少的数据(分类列表、配置项),用 unstable_cache 包一层:

import { unstable_cache } from 'next/cache';

const getCategories = unstable_cache(
  async () => db.category.findMany(),
  ['categories'],           // 缓存键:改业务逻辑时一起改这个
  { revalidate: 3600, tags: ['categories'] }
);

注意第二个参数——缓存键。AI 生成的代码经常用 ['data'] 这种毫无信息量的键,多个函数共用一个键会互相污染。键的命名规范:资源:版本:参数,比如 product-list:v1:page-2。

写操作后主动失效,别等 TTL:

import { revalidateTag } from 'next/cache';

await db.product.update({ where: { id }, data });
revalidateTag('products');   // 精准失效,比 revalidatePath 更便宜

vibe coder 最容易踩的坑:在 server component 里调了 headers() 或 cookies(),整页被迫动态渲染,之前做的缓存全白费。AI 很爱在布局里读 cookie 做个性化——把个性化部分拆成独立的 client component,主页面保持可缓存。

第三层:应用级缓存(Redis),以及三件套防护

当多个实例、多台机器要共享缓存时,就轮到 Redis(或内存缓存)上场。核心模式就一个:get-or-set。

async function getProductList(page) {
  const key = `products:v1:page:${page}`;
  const hit = await redis.get(key);
  if (hit) return JSON.parse(hit);          // 命中:直接返回
  const data = await db.product.findMany({ skip: (page-1)*20, take: 20 });
  await redis.set(key, JSON.stringify(data), 'EX', 300);  // 回填 5 分钟
  return data;
}

但生产环境有三个经典故障,AI 生成的代码一个都防不住,你得亲手补:

1. 缓存穿透:攻击者用不存在的 id 狂刷,请求全部打到数据库。解法:空值也缓存——查不到时存一个短 TTL 的空标记(如 EX 60 的 "__nil__"),别让「查不到」变成每次都查库。

2. 缓存击穿:热点 key 恰好过期,瞬间几百个请求同时回源,数据库被打挂。解法:互斥锁——只有一个请求去回源,其他的等 50ms 后重试读缓存;或者逻辑过期(value 里带过期时间戳,过期后异步刷新、同步返回旧值)。

3. 缓存雪崩:大批 key 同一时间过期(比如整点批量写入时设了相同的 TTL)。解法:TTL 加随机抖动——EX 300 + random(0,60),别让所有 key 死在同一秒。

另外一个血泪教训:Redis 挂了,应用不能跟着挂。所有 Redis 调用包 try/catch,异常时降级为直接查库——缓存是加速器,不是承重墙。

第四层:AI 结果缓存,2026 年的新必修课

vibe 项目越来越多调用 LLM/Agent,而模型调用又贵又慢——缓存 AI 结果的 ROI 比缓存数据库还高。有三层可以做:

1. 精确缓存:同一个 prompt 直接返回上次结果。用 prompt 的 hash 做 key,适合 RAG 问答、文档总结这类确定性任务。注意把 temperature 设 0,否则「相同输入不同输出」,缓存了也没意义。

2. 语义缓存:「北京天气怎么样」和「北京今天天气如何」是同一个问题。用 embedding 算相似度,超过阈值(如 0.95)就返回缓存答案。实现不复杂:问题向量存 pgvector/Redis,向量检索找最接近的历史问答。Agent 的高频重复问题(客服、站内搜索)用这招能砍掉 30-50% 的模型调用。

3. Prompt 缓存(provider 原生):Anthropic、OpenAI、Google 都支持对长 system prompt / 上下文做缓存——相同前缀只算一次钱。Agent 场景下 system prompt + 工具定义通常占输入的 70% 以上,开了 prompt 缓存等于常年 9 折。这是 2026 年最被低估的省钱开关,控制台里勾选一下就行。

什么时候不该缓存 AI 结果:个性化输出(「根据我的订单推荐」)、实时性要求高(股价、库存)、涉及隐私(医疗、财务问答)。省下的 token 抵不上一次错误答案的客诉。

失效策略:缓存最难的部分

有句老话:计算机科学只有两件事难——缓存失效和命名。失效策略就两种:

TTL 被动过期:简单,适合「旧一点没关系」的数据。从短 TTL 开始(5 分钟),业务反馈没问题再拉长。宁可缓存时间短、命中率低,也别一上来就 24 小时然后被「数据不同步」的客诉淹没。

写时主动失效:数据变了立刻清缓存。关键是「清什么」——精准失效依赖好的键设计。如果你的键是 products:v1:page:2,商品更新时要清所有 products:v1:page:*;Redis 用 SCAN 扫(别用 KEYS,会阻塞),或者维护一个「版本号」:products:v3,更新时版本号 +1,旧键自然作废——版本化失效是 vibe 项目最省心的方案。

诚实的建议:先只做 TTL,不做主动失效。等你真的被「用户改了东西页面没变」的客诉找上门,再补主动失效。过早做复杂的失效逻辑,是缓存 bug 的最大来源。

上线检查清单

发布前逐项打勾:

□ 静态资源带 hash + immutable,HTML 入口文件不缓存或短缓存
□ 支付/订单/余额类接口 Cache-Control: no-store
□ AI 默认生成的动态页面检查过:有没有误用 headers()/cookies() 导致整页动态
□ Redis 键有命名规范(资源:版本:参数),没有裸 ['data']
□ 空值缓存、TTL 抖动已加;Redis 调用有 try/catch 降级
□ 热点接口有互斥锁或逻辑过期,防击穿
□ Agent 的 system prompt 开了 provider 的 prompt 缓存
□ 写操作后清缓存的路径走通(先 TTL 版也行,但要明确写下来)
□ 压测过缓存挂掉的场景:停掉 Redis,应用照常工作只是变慢

一句话总结:缓存的每一层都是在回答「这份数据有多大把握是新的」。vibe 项目不需要一开始就四层拉满——先把 HTTP 缓存头和 5 分钟 TTL 的 Redis 做对,80% 的性能问题就消失了;剩下的,等用户量替你投票。

浏览项目广场发布你的项目

相关文章

API 网关流量控制与请求限流的抽象示意,象征对后端服务的保护
指南
一夜被脚本刷掉 300 美元:vibe 项目的 API 限流与配额实战

公网上的每个接口都会在某个深夜被超预期调用。这篇实战为一人团队搭建限流体系:算法选型(滑动窗口 vs 令牌桶)、四层防御、AI 接口烧钱专项防护、配额设计、429 响应规范、误伤排查,最后附上线检查清单。

后端工程安全与隐私部署上线
深色错误监控仪表盘界面,象征 vibe 项目的错误追踪与崩溃上报体系
指南
上线第一天用户白屏了你却最后一个知道:vibe 项目的错误监控与崩溃上报实战

每个 vibe 项目都会经历同一个黑色幽默时刻:网站白屏了,朋友比你的监控先告诉你。这篇实战为一人团队搭建完整错误监控体系:5 分钟 Sentry 最小闭环、错误边界、上报上下文设计、后端结构化日志、AI 调用专项防护、告警分级降噪,最后附上线检查清单。

调试排错后端工程部署上线
深色背景上的时钟与齿轮,象征 vibe 项目的定时任务调度
指南
定时任务是 vibe 项目的隐形杀手:从 setInterval 到生产级 cron 的完整实战

每个 vibe 项目迟早需要定时任务:每日数据同步、过期订单清理、账单对账、定时报告。AI 给你的第一个版本通常是 setInterval——开发够用,生产必死。这篇实战给出四种跑法的选型地图(应用内/Vercel Cron/GitHub Actions/Cloudflare),cron 表达式速查与时区坑,幂等性、防重叠分布式锁、失败重试与告警、可观测性 run log,以及 cron 接口的鉴权,最后附上线清单。

后端工程自动化独立开发