Promise.all()处理Firestore大量并行写入耗时过长问题
分析与解决方案
你遇到的这个现象其实和Firestore客户端的并发处理机制以及官方推荐的批量写入策略有关,我来帮你拆解清楚:
为什么total>500时耗时剧增?
虽然你用Promise.all发起了所有写入请求的并行调用,但Firebase Admin SDK的Firestore客户端会对并发请求数做限流处理——这是SDK内置的保护机制,避免触发Firestore后端的速率限制(你没看到请求失败,但后端会对高频请求自动排队)。当发起超过500个独立的add请求时,客户端无法同时处理这么多请求,后续请求会被放入等待队列逐个执行,直接导致整体耗时大幅上升。
而你提到count查询很快返回正确结果,是因为Firestore的count()查询是基于元数据的统计,不需要遍历实际文档,所以不管写入量多大,它都能快速返回最新统计值,这和写入请求的收尾处理是相互独立的流程。
优化方案:使用批量写入(WriteBatch)
Firestore官方推荐用WriteBatch处理大量写入操作,单个批量请求最多可包含500个写入操作,能把成百上千个独立请求合并为少数几个请求,既提升写入效率,又能避免客户端限流问题。
修改后的代码示例如下:
const admin = require('firebase-admin'); admin.initializeApp({credential: admin.credential.applicationDefault()}); const db = admin.firestore(); const main = async () => { const startTimeMs = Date.now(); const doc = {abc: "def"}; const total = 1000; const batchSize = 500; // 每个批次最多容纳500个写入操作 let batches = []; let currentBatch = db.batch(); for (let i=0; i<total; i++) { // 生成随机文档ID,和原代码中add()的行为完全一致 const docRef = db.collection('/test').doc(); currentBatch.set(docRef, doc); // 当批次满额或到达最后一个文档时,提交当前批次并重置 if ((i + 1) % batchSize === 0 || i === total - 1) { batches.push(currentBatch.commit()); currentBatch = db.batch(); } } // 等待所有批次写入完成 await Promise.all(batches); // 获取文档统计数 const countSnapshot = await db.collection('/test').count().get(); console.log("count", countSnapshot.data().count); const endTimeMs = Date.now(); console.log(`总耗时:${endTimeMs - startTimeMs}ms`); }; (async () => { await main(); })();
额外说明
- 批量写入的效率优势:每个
WriteBatch.commit()只会发起一个HTTP请求,不管批次里有多少个写入操作(上限500),这会比发起1000个独立请求快得多。 - 资源清理优化:批量写入减少了客户端需要处理的连接数,写完后客户端的资源清理也会更顺畅,不会出现
main函数看似执行完毕但实际耗时超长的情况。
你可以测试修改后的代码,不管total是10、100还是1000,耗时都会保持在合理范围内。
内容的提问来源于stack exchange,提问作者Jeff Huang
相关产品推荐
相关产品推荐

