You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署后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

排查方向与优化建议

  1. 区域匹配检查:确保Cloud Function与Firestore数据库部署在同一区域(当前设置了europe-west3,需确认Firestore是否也在该区域),跨区域访问会带来显著网络延迟。
  2. 冷启动优化:部署后首次执行耗时明显更高,后续复用连接后耗时下降,符合冷启动特征。可设置函数最小实例数或使用预热机制减少冷启动频率。
  3. 批量写入替代并行请求:改用Firestore的WriteBatch API将多个写入操作打包成单个请求,减少网络往返次数,提升大数量写入的性能。
  4. 序列化优化:代码中JSON.parse(JSON.stringify(item))的深拷贝方式对大对象开销较高,可改用更高效的深拷贝方法,或直接传递对象(无引用问题时)。
  5. 资源配置升级:检查Cloud Function的内存、CPU配置,默认配置可能不足以处理大文档的序列化与写入,提升内存配额通常会同步提升CPU性能。
  6. 数据库监控:在Firebase控制台查看Firestore写入延迟指标,确认瓶颈来自数据库端还是函数与数据库的网络链路。

内容的提问来源于stack exchange,提问作者Jonas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 22:29:54