如何优化Firestore子集合查询?提升多文档关联查询性能
Firestore子集合查询性能优化方案
1. 并行化所有异步查询
你的当前实现是串行执行每个父文档的子集合查询:先查完一个文档的classification,再查manufacturers,然后才处理下一个父文档,总耗时等于所有查询时间的总和,这是性能瓶颈的核心原因。
改成并行执行所有查询能大幅缩短耗时:
- 对单个父文档,同时发起
classification和manufacturers的查询(用Promise.all) - 对所有父文档,同时启动它们的查询任务
优化后的代码示例:
// 定义处理单个父文档的异步函数 const processPurchaseDoc = async (doc: QueryDocumentSnapshot) => { const basePurchase = { ...(doc.data() as Purchase), classification: [], manufacturers: [], }; // 并行获取两个子集合数据 const [classification, manufacturers] = await Promise.all([ getClassificationDocs(cnpj, doc.id), getManufacturersDocs(cnpj, doc.id) ]); return { ...basePurchase, classification, manufacturers }; }; // 并行处理所有父文档 const finalDocs = await Promise.all(data.docs.map(processPurchaseDoc));
2. 反范式化数据(嵌入子集合内容)
如果classification和manufacturers数据更新频率低,且单条purchases文档加上嵌入数据后不超过Firestore的1MB文档限制,可以直接把这些子集合的数据嵌入到父文档中。
这样只需一次查询就能获取所有数据,彻底消除多次子集合查询的开销。需要注意:
- 子集合数据更新时,要同步更新父文档中的嵌入内容(可通过Cloud Functions自动触发同步)
- 避免嵌入过大的数据集,防止触发文档大小限制
3. 缓存重复查询结果
如果同一个cnpj + parentDocId的组合会被多次查询,可以给子集合查询函数加上缓存逻辑,减少重复的数据库请求:
// 用Map存储缓存,键为`cnpj:parentDocId`的组合 const classificationCache = new Map<string, any[]>(); const manufacturersCache = new Map<string, Manufacturer[]>(); const getManufacturersDocs = async ( cnpj: string, parentDocId: string ): Promise<Manufacturer[]> => { const cacheKey = `${cnpj}:${parentDocId}`; if (manufacturersCache.has(cacheKey)) { return manufacturersCache.get(cacheKey)!; } const manufacturersRef = collection( db, "company", cnpj, "purchases", parentDocId, "manufacturers" ); const manufacturersDocs = await getDocs(manufacturersRef); const result = manufacturersDocs.docs.map((item) => ({ ...(item.data() as Manufacturer) })); manufacturersCache.set(cacheKey, result); return result; };
4. 限制子集合查询的数据量
如果子集合中的文档数量很多,但你只需要部分数据(比如最新的N条),可以在查询时添加limit()限制返回的文档数,减少数据传输和处理时间:
const manufacturersRef = collection( db, "company", cnpj, "purchases", parentDocId, "manufacturers" ).limit(10); // 仅返回前10条数据
内容的提问来源于stack exchange,提问作者José Carlos
相关产品推荐
相关产品推荐

