跳到主内容
笔记 Article

Cloudflare 免费额度的真实边界

KV 每天 1000 次写、D1 每天 500 万行读——这些数字看起来很大,但踩过一次坑才知道,delete 也算写。一次事故换来的六条硬规则。

2 分钟阅读 0 次浏览

一次真实事故

某次部署后,我写了一个「清理过期缓存」的脚本,遍历 KV 里所有 cache:* 前缀的键然后逐个 delete。

本地测试通过,线上跑完也没报错。第二天早上,全站 API 开始返回 500——不是代码错,是 KV 写入配额被判定用尽。

原因很简单:KV 免费版每天 1000 次写入,而 delete 同样计入写入配额。那晚脚本删了 3000 多个键。

六条硬规则

1. delete 就是写

官方文档写得很清楚,但心理上很难接受。任何「批量清理」的念头,在动手前先问一句:这批操作会消耗多少次写?

2. 用版本号代替删除

缓存失效不要删键,改成把版本号 +1:

// ❌ 错误:N 个键 = N 次写
for (const key of allCacheKeys) await kv.delete(key);

// ✅ 正确:1 次写,旧条目靠 TTL 自然过期
await kv.put('cache:version', String(Date.now()), { expirationTtl: 86400 });

读取时把版本号拼进 key:cache:v{version}:{path}。旧版本的键没人再读,到期自动消失,全程 0 次删除。

3. TTL 是免费的

expirationTtl 不消耗额外配额,是这套系统里最便宜的资源。所有缓存都必须带 TTL,没有例外。

4. D1 的读很便宜,写很贵

免费版 D1 每天约 500 万行读、10 万行写。读基本用不完,但一次批量插入 1000 行就是 1000 次写。填充示例数据时要注意上限。

5. 静态优先

能塞进 R2(无请求数限制、只计存储和出流量)的就不要放 D1。图片、字体、主题包资源全部走 R2 直链。

6. 给自动化加保险

部署脚本里我加了一道 assertKvDeleteSafe():任何删除操作超过 50 次直接抛错中止。用代码把红线焊死,因为人会忘,代码不会。

一句话总结

在免费额度上做产品,真正的成本不是钱,是「你的架构必须假设每次写都很贵」。

这个约束听起来讨厌,但它逼出来的设计(版本号缓存、TTL 兜底、静态优先)其实比随手删键更干净。

雄

雄哥

独立开发者 · 长期写作者

关于