上个月某天打开托管平台的用量页,一排红色。
最显眼的一项叫 ISR Writes,免费额度 20 万次,实际用了 100 万次,超了 5 倍。紧跟着是回源流量超 2.3 倍、CDN 请求超 2 倍、函数 CPU 时间超 1.9 倍、ISR Reads 超 1.8 倍。五项同时爆表,平台已经在页面顶部提示:再这样下去,项目会被暂停。
我的第一反应和大多数人一样:是不是被刷了?是不是图片太多、站点太大,该花钱升级了?
后来发现,这两个猜测都不对。真正让额度爆掉的,是我自己半年前随手写下的两行配置。
先排除最像答案的那个答案
博客有四千多篇文章,配图、幻灯片、微信备份加起来体积不小。所以“存储太大”是最容易让人信服的解释,也是最危险的:一旦信了,接下来的动作就是买套餐、迁图床、删内容,花了钱,问题却还在。
我先把用量页上所有指标抄下来,分成红的和蓝的两组。结果很说明问题:部署存储只用了 10%,数据传输 55%,函数调用次数 63%,全都很宽裕。爆掉的五项,没有一项和“体积”有关,全部和“计算”和“回源”有关。
这一步看起来简单,但它决定了后面所有方向。排查额度问题,先看哪些指标没超,往往比看哪些超了更快找到方向。 没超的那一组,替你排除了一半的错误答案。
真正的根因:一个只预生成 100 篇的博客
ISR 是 Next.js 的一种折中机制:页面可以在构建时提前生成,也可以在用户访问时再生成,生成后缓存一段时间,过期再重算。它的设计初衷很好,但计费方式决定了它在小站上很容易用错。
我的文章详情页里有这样一段逻辑:构建时只预生成最新的 100 篇,其余的等有人访问时再说。当初这么写,是为了让部署快一点。
问题是,博客有 4667 篇。剩下的 4567 篇,每一篇被第一次访问时,平台都要临时跑一次渲染、回源取数据、写一次缓存。这就是一次 ISR Write,加一份回源流量,加一段 CPU 时间。而访问这些老文章的,大多不是人,是搜索引擎和各种爬虫,它们会一篇一篇地扫过去。
第二个配置把问题放大了:缓存有效期设成了 3600 秒。意味着那 100 篇预生成的文章,每小时都要被重新验证一次,ISR Reads 就这样持续往上涨。
第三个放大器是 robots.txt。它对全站写着“允许抓取”,而站内的搜索页、微信备份页和 API 接口都是强制动态的,每被爬一次就跑一次函数。爬虫不知道这些页面很贵,它只知道可以爬。
三件事叠在一起,就是一份看起来像“被攻击”的用量报告。没有人攻击我,是我的配置在邀请爬虫替我花钱。
修法很小,代价要提前算清楚
想清楚之后,改动其实只有四处:
- 文章详情页的缓存有效期从 3600 改成不过期,内容只在重新部署时更新;
- 预生成范围从“最新 100 篇”改成“全部文章”;
- 首页同样改为不过期;
- robots.txt 禁止爬虫访问搜索页、备份页和 API,sitemap 里也去掉搜索页,更新频率从每天改成每周。
这里有个必须提前想明白的代价:全量预生成意味着每次部署的产物从约 626 MB 涨到约 1.2 GB,构建时间也会变长。这个代价能不能接受,不能凭感觉,要回到刚才那组蓝色指标:存储只用了 10%,翻一倍也才 20%。用一项很宽裕的资源,去换掉五项已经爆表的资源,这笔账是划算的。
好的修复不是把问题消掉,而是把成本从紧张的地方挪到宽裕的地方。
每个改动前我都给原文件留了一份带日期的备份。回滚的方法写成一句话:把备份拷回去,重新部署一次。改生产配置之前先想好怎么退回来,这一步花不了一分钟,却决定了出事时你是慌还是稳。
中途还踩了一个坑
第一次部署失败了,第二次也失败了。
原因和这次的修复毫无关系:项目根目录里躺着一个之前为“迁移到另一家云平台”准备的配置文件,它引用了一个根本没安装的依赖。而 TypeScript 的检查范围恰好覆盖整个根目录,于是类型检查直接挂掉,整个构建中止。
把那个文件挪进单独的迁移目录、让 TypeScript 排除它,第三次部署五分多钟后成功。
这个坑让我记下了一条规矩:根目录不放依赖没装好的代码文件。 预备方案可以存在,但要待在自己的抽屉里,不要摊在主干上等着被别人误碰。
修完之后怎么确认真的修好了
部署成功不等于问题解决。我做了一张验收表,逐项去线上看:
- 首页响应头里出现了缓存命中标记,说明页面不再每次重算;
- 之前一张一直 404 的图片恢复成 200,这次部署顺手把漏传的图片也带上去了;
- robots.txt 的三条禁止规则在线上生效;
- sitemap 里有 4676 条链接,搜索页一条都没有。
几天后再看用量页,ISR 的两项写入和读取基本停止增长。红色没有立刻消失,因为当月已经用掉的不会退回来,但曲线平了,这才是真正的信号。
这件事真正教会我的
回头看,这次额度危机里没有任何高深的技术。所有信息都摆在用量页上,所有答案都在自己写过的代码里。真正耽误事的,是一开始那个“肯定是站太大了”的直觉。
这种直觉很自然,因为“体积大”是一个看得见摸得着的东西,而“缓存策略”藏在一行不起眼的数字后面。人总是先怀疑看得见的东西。
所以下次再遇到类似的额度或账单问题,我会这样做:
- 先把所有指标抄全,分成超了的和没超的,用没超的那一组排除错误方向;
- 不急着花钱或删东西,先问一句:这个指标是被谁、在什么时候、因为哪行配置消耗的;
- 修复前想清楚代价落在哪一项资源上,确认那项资源足够宽裕;
- 每个改动留好备份,回滚方法写成一句话;
- 部署成功后做线上验收,过几天再回来看曲线,而不是看到绿色就收工。
一个博客每月的额度不值多少钱,但这套排查顺序,在任何会按用量计费的地方都用得上。