你的 Firebase 项目突然全线挂掉:Firestore 读取报 8 RESOURCE_EXHAUSTED: Quota exceeded、Functions 返回 503、Hosting 抛配额错误——但你一行代码都没改。Console 红色横幅写着 Quota exceeded for quota metric ...。这基本只有两种情况:Spark(免费)计划某项指标打到当天硬上限,或者某段失控循环把额度烧光了。
最快的修复:打开 Firebase Console → Usage and billing,找出那个停在 100% 的指标。如果是 Firestore 读/写,每日配额在美国太平洋时间午夜前后重置——只要那个吃额度的东西已经停下,服务过一夜会自动恢复。如果等不了,升级到 Blaze(按量付费),并在同一次操作里设好预算告警(见下方 Step 2)。但别停在这里:一定要找出是什么烧光了配额,否则明天额度重置后又会被烧爆。
理解关键:Firebase 不是单一额度,而是几十个独立计费的指标——Firestore reads / writes / deletes / 存储 GB、Functions invocations / GB-seconds、Hosting 数据传输、Cloud Storage 操作、phone auth 验证。任一项打到 cap,依赖它的功能就跟着挂。
2026 年的两个变化(先看这个)
有两项计划变动会让旧的 Firebase 教程失效,排查前先核对你的项目:
- Cloud Functions 现在必须用 Blaze 计划。 Spark 上已经无法部署或运行 Cloud Functions。如果你的 Functions 还在跑,说明你早就在 Blaze 上了——也就是说你没有硬上限,你面对的是一段在烧真金白银的失控循环。(旧的”Spark 免费 125K invocations/月”已不存在;Blaze 免费额度是 2M invocations/月,超出后计费。)
- Cloud Storage 已于 2026 年 2 月 3 日从 Spark 移除。 Spark 项目无法访问默认 bucket,Storage API 调用返回
402/403。如果你的存储坏了,那不是配额问题,而是这次计划变动——要用 Cloud Storage 必须升 Blaze。
所以动手前先确认:你在 Spark(硬性每日上限,不能透支)还是 Blaze(免费额度用完后计费,会透支)。Console 左上角齿轮 / Usage and billing → Details & settings 能看到你的计划。
免费额度(截至 2026 年 6 月)
以下就是你可能撞到的红线。Spark 是硬封顶(服务停摆,不能透支);Blaze 给同样的免费额度,超出后开始计费。
| 指标 | 免费额度 | 计划 |
|---|---|---|
| Firestore reads | 50K / 天 | Spark + Blaze |
| Firestore writes | 20K / 天 | Spark + Blaze |
| Firestore deletes | 20K / 天 | Spark + Blaze |
| Firestore 存储数据 | 1 GiB 总量 | Spark + Blaze |
| Hosting 存储 | 10 GB | Spark + Blaze |
| Hosting 数据传输 | 360 MB / 天 | Spark + Blaze |
| Functions invocations | 2M / 月 | 仅 Blaze |
| Functions GB-seconds | 400K / 月 | 仅 Blaze |
| Cloud Storage 存储 | 5 GB | 仅 Blaze |
| Cloud Storage 下载 | 1 GB / 天 | 仅 Blaze |
| Phone auth 验证 | 按地区,每日小额度 | Spark + Blaze |
注意:旧的 Hosting “10 GB/月带宽”已不是现在的计费方式——Hosting 免费传输现在是 360 MB/天(30 天大约 10.5 GB,但它是按天卡的,所以哪怕你整月流量很小,某天突然火一下也会触发)。
你属于哪一种情况?
| 症状 | 最可能的原因 | 跳转 |
|---|---|---|
| reads 到 100%,用户数正常 | 客户端监听循环或缺 limit() | 原因 3、7 / Step 5、7 |
| reads 暴涨 100x,用户数没变 | 安全规则开放 + 爬虫/攻击者 | 原因 6 / Step 3 |
| Functions invocations 上百万 | Function 自触发循环 | 原因 2 / Step 4 |
| Hosting 传输对照流量异常大 | 大文件 / 视频走 Hosting 分发 | 原因 4 / 预防 |
Storage 报 402/403,之前还能用 | Cloud Storage 退出 Spark(2026 年 2 月) | 升级 Blaze |
| 存储超 1 GiB,用户却没几个 | 没设 TTL,日志/会话堆积 | 原因 5 |
常见原因
按命中率从高到低排列。
1. Spark 计划某项打到每日硬上限
Spark 是硬封顶——你不能透支,指标到线就停。最常见的是 Firestore reads(50K/天)。只要吃额度的东西停下来,服务会在下一次每日重置(太平洋时间午夜)后恢复。
如何判断:Console → Usage and billing → 看哪一项 100%。客户端日志里的报错:8 RESOURCE_EXHAUSTED: Quota exceeded。
2. Functions 死循环烧调用数
经典反模式——Firestore 写触发器又往同一个 collection 写:
// 写 Firestore 触发 onWrite → 又写 Firestore → 又触发 onWrite → ...
exports.onUserUpdate = onDocumentWritten('users/{uid}', async (event) => {
await db.doc(`users/${event.params.uid}`).update({...}); // 触发自身
});
在 Blaze 上(Functions 现在都跑在这),这能在几小时内烧完 2M 免费 invocations,然后整月剩下的全计费。
如何判断:Console → Functions → 看单个函数的 invocations 异常高(百万级)。Cloud Logging 里也能看到。
3. 客户端死循环读 Firestore
function Comp() {
useEffect(() => {
onSnapshot(query, snap => {
setItems(snap.docs); // 触发 re-render → useEffect 又跑 → 又 subscribe
});
}); // 缺 deps array
}
几个活跃用户就能在一小时内把 50K reads 清零。
如何判断:Console → Firestore → Usage → reads/sec 相对活跃用户数异常地高。
4. 大文件 / 视频撑爆 Hosting 数据传输
把一个 100 MB 的视频放 Firebase Hosting,几百个人看,几分钟就超过 360 MB/天 的免费传输。
如何判断:Hosting 数据传输对照你的真实访问量异常大。
5. 没设 Firestore TTL,老数据堆积
Firestore 里存的 logs / sessions / analytics 没清理,迟早把 1 GiB 存储上限撑爆。
如何判断:存储超过 1 GiB,但真实用户数据量很小。
6. 爬虫 / 攻击者扫数据
宽松的 Firestore 安全规则让任何拿到你客户端配置的人都能直接 query 任意 collection。爬虫找到 endpoint 后,reads/writes 双爆。
如何判断:用户数没变,但 reads 暴涨 100x。看 Firestore Usage 曲线有没有在没上线新功能的情况下出现台阶式跳升。
最短修复路径
Step 1:定位是哪一项爆了
Firebase Console → Usage and billing
→ 看 Firestore / Functions / Hosting / Storage / Auth 各项
→ 到 100% 的那项(Blaze 上则是曲线突然拉直上扬的那项)就是元凶
看仪表盘,别凭感觉猜。
Step 2:紧急止血——升 Blaze + 设预算告警
如果你需要立刻恢复(Spark 要等太平洋时间午夜才重置),升级到 Blaze,并在同一次操作里设好预算告警:
1. Google Cloud Console → Billing → Budgets & alerts → Create budget
2. Budget amount:你愿意承受的最大月支出(比如 $20)
3. Alert thresholds:50%, 90%, 100%
4. Alert recipients:你的 email
5. 可选硬停:把 Pub/Sub 预算通知接到一个 Cloud Function,
调用 projects.updateBillingInfo 关掉计费
现实提醒:预算告警不会封顶消费——它只是给你发邮件。唯一真正的硬停是 Pub/Sub 触发的”关闭计费”自动化,而且它生效前已经发生的费用照样要付。(Google 官方的封顶指南 有这个 Cloud Function 的完整写法。)如果想要零透支风险的硬上限,那就留在 Spark,改去修失控源。
Step 3:定位并停掉失控源
失控源 = 客户端循环 / Function 循环 / 攻击者
Function 循环:
Console → Functions → 找 invocations 异常那个 → Disable
(或删掉部署,改好再重新 deploy)
客户端循环:
回滚最新前端部署
或 hotfix useEffect(加 deps array——见 Step 5)
攻击者 / 规则开放:
立刻全锁死,再按需逐项放开:
// 紧急锁死——贴上、部署、喘口气,再选择性放开
match /{document=**} { allow read, write: if false; }
部署:firebase deploy --only firestore:rules。
Step 4:给 Functions 加防呆
exports.onUserUpdate = onDocumentWritten('users/{uid}', async (event) => {
// 如果 _serverUpdate flag 已经设上,说明这次写是我们自己触发的——直接退出
const after = event.data?.after.data();
if (after?._serverUpdate) return;
await db.doc(`users/${event.params.uid}`).update({
derived: ...,
_serverUpdate: true, // 打标记,让下一次触发短路掉
});
});
更彻底的做法:把源数据和派生数据拆成两个 collection,让写入路径喂不回触发器。
Step 5:客户端 useEffect 加 deps
useEffect(() => {
const unsub = onSnapshot(query, snap => setItems(snap.docs));
return () => unsub(); // 卸载时清理监听
}, []); // 空 deps:只订阅一次,而不是每次渲染都订阅
Step 6:收紧 Firestore 安全规则(最小权限)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{uid} {
allow read, write: if request.auth.uid == uid;
}
match /posts/{post} {
allow read: if true;
allow write: if request.auth != null
&& request.resource.data.authorId == request.auth.uid;
}
}
}
部署:firebase deploy --only firestore:rules。部署前先在 Console → Firestore → Rules → Rules Playground 里测一遍规则。
Step 7:减少不必要的 reads
// 慢:每次都全量 query
const all = await getDocs(collection(db, 'posts'));
// 快:分页 + cache
const first = await getDocs(query(
collection(db, 'posts'),
orderBy('createdAt', 'desc'),
limit(20)
));
// React 里用 SWR 或 TanStack Query 缓存并去重 fetch
如何确认已修好
- Console → Usage and billing:之前到 100% 的指标应该停止增长(Blaze 上则是 reads/sec 曲线几分钟内回落到基线)。
- Functions 标签页:调用速率回到正常,没有单个函数飙升。
- 客户端:日志里不再出现
8 RESOURCE_EXHAUSTED,读取成功。 - Spark 上,完整服务会在下一次太平洋时间午夜重置后恢复——但前提是失控源已经死掉。如果指标重新填满、第二天又触发,说明你只压住了症状没堵住源头:回到 Step 3。
预防建议
- 上线前先在 Google Cloud 设预算告警(50% / 90% / 100%);如果在 Blaze 上,再接好 Pub/Sub 的”关闭计费”自动化。
- Firestore 规则默认 deny,只放开你确实需要的路径。绝不要上线
allow read, write: if true;。 - Function 永远不要订阅自己也会写的 collection。如果非要,用 flag 打断循环(见 Step 4)。
useEffect一定要有 deps array;用eslint-plugin-react-hooks强制。- Firestore query 永远加
limit(),开发阶段也要——对一个还在增长的 collection 做无界getDocs就是一颗慢动作配额炸弹。 - 大文件 / 视频别走 Firebase Hosting(免费传输只有
360 MB/天)。用 R2 / S3 + CDN。 - 给 logs / sessions / analytics 加 Firestore TTL policy,让存储自动清理。
- 监控 reads/sec 和 writes/sec,异常 spike 立刻告警。
- 上线前估算月成本——比如 Firestore reads 是
(每日 reads × 30 ÷ 100000) × $0.06 + ...——心里有个”正常值”的概念。
常见问题
我什么都没改,Firebase 怎么就挂了? 某个免费档指标打到了每日上限——通常是 Firestore reads(50K/天),背后是客户端监听循环、Function 循环,或利用开放规则的爬虫。它积累了几个小时,然后一下子越线。
它会自己恢复吗? 在 Spark 上,Firestore 每日配额在美国太平洋时间午夜前后重置,所以服务过夜会恢复——但前提是吃额度的东西已经停了。如果它还在跑,第二天又会触发。
Spark 还是 Blaze 能保我不被账单吓到? Spark。它不能透支,指标到 cap 就停。Blaze 给同样的免费额度,但之后无硬上限地计费,除非你自己搭好 Pub/Sub 的”关闭计费”自动化。单靠预算告警只会发邮件。
我的 Cloud Storage / Cloud Functions 突然返回 402/403 或部署不了。是配额吗? 不是——这两个现在都要 Blaze 计划。Cloud Storage 于 2026 年 2 月 3 日退出 Spark,Functions 也早就要 Blaze 了。把项目升到 Blaze(并设预算告警)才能用。
怎么找出到底是哪个函数在循环? Console → Functions 会显示每个函数的调用数,按它排序。把 Cloud Logging 过滤到那个函数名,能看出每次运行是不是又触发了一次写。在改代码之前,先 Disable 那个函数止血。
在 Firebase Console 里翻数据会算进我的读配额吗? 会——Console 渲染文档时会去读 Firestore,这也计数。已经接近 cap 时,别在 Data 标签页反复刷大 collection。