部署后Cloud Function写入Firestore速度远慢于本地的求助
Cloud Function部署后Firestore写入性能大幅下降问题
问题背景
我有一个Cloud Function,用于生成数据并存储到Firestore数据库,部分文档体积较大(保存为普通JSON文件时为200-500kB)。
部署后的Function运行速度远慢于本地运行:本地模拟器连接真实部署的数据库执行写入,耗时约1-3秒;但部署后的Function执行相同写入操作,平均耗时20-30秒,数据库写入的耗时差异尤为显著。
测试代码
我编写了以下代码复现问题并进行基准测试:
import * as admin from "firebase-admin"; import * as functions from "firebase-functions"; import { v4 as uuidv4 } from 'uuid'; function generateTestData() { const object: any = { children: [] }; for (let i = 0; i < 100; i++) { object.id = uuidv4(); object[`attribute${i}`] = uuidv4(); } for (let i = 0; i < 100; i++) { const childObject: any = {}; for (let j = 0; j < 100; j++) { childObject[`attribute${j}`] = uuidv4(); } object.children.push(childObject); } return object; } async function storeTestData() { const items = []; for (let i = 0; i < 21; i++) { items.push(generateTestData()); } const proms = items.map((item: any, index) => { const children = item.children; item.children = undefined; return [ admin.firestore().collection("Items").doc(item.id).set(JSON.parse(JSON.stringify(item)), { merge: true }), admin.firestore().collection("Items").doc(item.id).collection("Children").doc("Children").set(JSON.parse(JSON.stringify({ children: children })), { merge: true }), ]; }).reduce((acc, val) => acc.concat(val), []) ?? []; try { await Promise.all(proms); } catch (error) { console.error("Error", error); } return; } export const benchmarkFunctionWrites = functions.https.onRequest(async (req, res) => { const t1 = new Date().getTime(); await storeTestData(); const duration = new Date().getTime() - t1; console.log(`Took ${duration}ms`); res.status(200).send({ duration }); });
基准测试脚本
使用以下脚本执行多次测试取平均值:
(async () => { const functionUrl = "FUNCTION_URL" const writeDurations = []; for (let i = 0; i < 10; i++) { const res = await fetch(functionUrl); const json = await res.json(); const duration = json.duration; console.log("Duration:", duration); writeDurations.push(duration); } console.log("Average:", writeDurations.reduce((a, b) => a + b, 0) / writeDurations.length); })()
测试结果
本地模拟器测试输出
Duration: 1047 Duration: 1079 Duration: 1566 Duration: 1591 Duration: 1612 Duration: 1856 Duration: 2570 Duration: 2238 Duration: 2933 Duration: 3690 Average: 2018.2
部署后Function测试输出
Duration: 33389 Duration: 26406 Duration: 24720 Duration: 33133 Duration: 24712 Duration: 28892 Duration: 24572 Duration: 32681 Duration: 27282 Duration: 24564 Average: 28035.1
编辑:优化测试后的结果
根据建议,调整测试代码,在同一个Function实例中多次顺序执行写入操作,并减小了测试数据体积(避免了13 INTERNAL: Received RST_STREAM with code 2错误,但原性能问题仍存在)。
调整后的测试代码
import * as admin from "firebase-admin"; import * as functions from "firebase-functions"; import { v4 as uuidv4 } from 'uuid'; function generateTestData() { const object: any = { children: [] }; for (let i = 0; i < 50; i++) { object.id = uuidv4(); object[`attribute${i}`] = uuidv4(); } for (let i = 0; i < 50; i++) { const childObject: any = {}; for (let j = 0; j < 50; j++) { childObject[`attribute${j}`] = uuidv4(); } object.children.push(childObject); } return object; } async function storeTestData() { const items = []; for (let i = 0; i < 21; i++) { items.push(generateTestData()); } const proms = items.map((item: any, index) => { const children = item.children; item.children = undefined; return [ admin.firestore().collection("Items").doc(item.id).set(JSON.parse(JSON.stringify(item)), { merge: true }), admin.firestore().collection("Items").doc(item.id).collection("Children").doc("Children").set(JSON.parse(JSON.stringify({ children: children })), { merge: true }), ]; }).reduce((acc, val) => acc.concat(val), []) ?? []; try { await Promise.all(proms); } catch (error) { console.error("Error", error); } return; } export const benchmarkFunctionWrites = functions.region('europe-west3').https.onRequest(async (req, res) => { const results: number[] = []; async function benchmarkCycle() { try { const t1 = new Date().getTime(); await storeTestData(); const duration = new Date().getTime() - t1; results.push(duration); console.log(`Took ${duration}ms`); } catch (error) { console.error(error); } } await benchmarkCycle(); await benchmarkCycle(); await benchmarkCycle(); await benchmarkCycle(); await benchmarkCycle(); res.status(200).send({ durations: results }); }); export const benchmarkFunctionWritesUs = functions.https.onRequest(async (req, res) => { const t1 = new Date().getTime(); await storeTestData(); const duration = new Date().getTime() - t1; console.log(`Took ${duration}ms`); res.status(200).send({ duration }); });
调整后的测试结果
部署后Function测试输出
Durations: [ 14136, 6731, 6789, 6587, 6291 ] Durations: [ 12208, 7290, 7793, 7352, 6800 ] Durations: [ 6821, 6670, 6476, 6820, 6420 ] Durations: [ 6442, 6892, 5905, 6411, 6708 ] Durations: [ 6384, 6871, 6058, 6654, 6278 ] Durations: [ 7898, 7196, 6969, 6805, 6921 ] Durations: [ 6116, 6507, 6508, 6721, 6871 ] Durations: [ 6987, 6876, 6053, 6346, 6936 ] Durations: [ 6253, 7295, 6128, 6282, 6812 ] Durations: [ 6967, 6870, 6428, 6682, 6371 ] Average: 6951.7
本地模拟器测试输出
Durations: [ 4274, 962, 987, 936, 960 ] Durations: [ 976, 1029, 944, 914, 935 ] Durations: [ 986, 955, 947, 1003, 950 ] Durations: [ 1052, 908, 909, 918, 904 ] Durations: [ 945, 918, 1054, 892, 929 ] Durations: [ 933, 910, 1086, 918, 970 ] Durations: [ 933, 949, 915, 903, 919 ] Durations: [ 945, 952, 1015, 936, 899 ] Durations: [ 925, 895, 1009, 966, 945 ] Durations: [ 939, 909, 960, 960, 907 ] Average: 1015.7
排查方向与优化建议
- 区域匹配检查:确保Cloud Function与Firestore数据库部署在同一区域(当前设置了
europe-west3,需确认Firestore是否也在该区域),跨区域访问会带来显著网络延迟。 - 冷启动优化:部署后首次执行耗时明显更高,后续复用连接后耗时下降,符合冷启动特征。可设置函数最小实例数或使用预热机制减少冷启动频率。
- 批量写入替代并行请求:改用Firestore的
WriteBatchAPI将多个写入操作打包成单个请求,减少网络往返次数,提升大数量写入的性能。 - 序列化优化:代码中
JSON.parse(JSON.stringify(item))的深拷贝方式对大对象开销较高,可改用更高效的深拷贝方法,或直接传递对象(无引用问题时)。 - 资源配置升级:检查Cloud Function的内存、CPU配置,默认配置可能不足以处理大文档的序列化与写入,提升内存配额通常会同步提升CPU性能。
- 数据库监控:在Firebase控制台查看Firestore写入延迟指标,确认瓶颈来自数据库端还是函数与数据库的网络链路。
内容的提问来源于stack exchange,提问作者Jonas
相关产品推荐
相关产品推荐

