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

如何加速Firebase Functions+Express应用的慢请求(耗时超8秒)

Firebase Cloud Functions 请求性能优化方案

问题背景

我们的后端基于Node.js、Express和Firebase构建,目前所有请求(包括简单查询)耗时普遍超过8秒,已排除冷启动问题(后续请求同样缓慢)。尝试过拆分接口文件、移除第三方库,性能无改善。

环境配置信息

package.json 配置

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "serve": "firebase serve --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "14"
  },
  "dependencies": {
    "@google-cloud/storage": "^5.8.2",
    "@sendgrid/mail": "^7.2.1",
    "algoliasearch": "^4.3.0",
    "bcrypt": "^5.1.0",
    "busboy": "^0.3.1",
    "cookie-parser": "^1.4.5",
    "cors": "^2.8.5",
    "dayjs": "^1.10.4",
    "dotenv": "^8.2.0",
    "easy-soap-request": "^4.1.3",
    "express": "^4.17.1",
    "firebase": "^7.15.5",
    "firebase-admin": "^8.6.0",
    "firebase-functions": "^3.23.0",
    "fs-extra": "^9.0.1",
    "jwt-decode": "^2.2.0",
    "moment": "^2.29.1",
    "request": "^2.88.2",
    "sharp": "^0.25.4",
    "sib-api-v3-sdk": "^8.4.2",
    "uuid": "^8.2.0",
    "xml-js": "^1.6.11"
  },
  "devDependencies": {
    "firebase-functions-test": "^0.1.6"
  },
  "private": true
}

index.js 核心代码

require("dotenv").config();
const functions = require("firebase-functions");
const express = require("express");
const app = express();

const cookieParser = require("cookie-parser");
const cors = require("cors");

app.use(cors());
app.use(cookieParser());
app.use(express.json());

const dashboardRoutes = require("./routes/dashboardRoutes");
const userRoutes = require("./routes/userRoutes");
const pagesRoutes = require("./routes/pagesRoutes");
const orderRoutes = require("./routes/orderRoutes");
const cartRoutes = require("./routes/cartRoutes");
const wishlistRoutes = require("./routes/wishlistRoutes");
const authRoutes = require("./routes/authRoutes");
const storeRoutes = require("./routes/storeRoutes");
const createSellerRoutes = require("./routes/createSellerRoutes");

app.use("/", pagesRoutes);
app.use("/user", userRoutes);
app.use("/dash", dashboardRoutes);
app.use("/order", orderRoutes);
app.use("/cart", cartRoutes);
app.use("/wishlist", wishlistRoutes);
app.use("/auth", authRoutes);
app.use("/s", storeRoutes);
app.use("/cr", createSellerRoutes);

const {
  cron_job1,
  cron_job2,
  cron_job3,
  cron_job4,
} = require("./triggers/search_triggers_and_cron_jobs"); // 非实际文件名

const { /* other cron jobs */ } = require("./cron-jobs");
const {
  update_exchange_currency_rates,
} = require("./cron-jobs/currency_exchange_rates");
const { reset_product_visits } = require("./triggers/products");
const { Home } = require("./handlers/pages");
const { db } = require("./util/admin");
const { product_basic_obj } = require("./util/product_basic_obj");

exports.apis = functions.https.onRequest(app);

// 示例慢请求接口逻辑
app.get("/test-home", (req, res) => {
  let content = {};
  db.collection("products")
    .where("status", "==", "active")
    .orderBy("visited", "desc")
    .limit(20)
    .get()
    .then((data) => {
      content.popular_today = [];
      data.forEach((x) => {
        content.popular_today.push(product_basic_obj(x.data()));
      });
      return db
        .collection("products")
        .where("status", "==", "active")
        .orderBy("todaysSales", "desc")
        .limit(20)
        .get();
    })
    .then((data) => {
      content.hot_today = [];
      data.forEach((x) => {
        content.hot_today.push(product_basic_obj(x.data()));
      });

      return db.collection("departments").get();
    })
    .then((data) => {
      content.departments = [];
      data.forEach((x) => {
        content.departments.push(x.data());
      });

      return db
        .collection("departments")
        .orderBy("products_sold_today", "desc")
        .limit(6)
        .get();
    })
    .then((data) => {
      content.top_departments = [];
      data.forEach((x) => {
        content.top_departments.push(x.data());
      });
      return res.status(200).json(content);
    });
});

// cron jobs 导出
exports.cron_job1 = cron_job1;
exports.cron_job2 = cron_job2;
exports.cron_job3 = cron_job3;
exports.cron_job4 = cron_job4;

执行日志

i  functions: Beginning execution of "us-central1-apis"
⚠  Google API requested!
   - URL: "https://www.googleapis.com/oauth2/v4/token"
   - Be careful, this may be a production service.
i  functions: Finished "us-central1-apis" in ~9s
i  functions: Finished "us-central1-apis" in ~8s
i  functions: Beginning execution of "us-central1-apis"
i  functions: Finished "us-central1-apis" in ~7s
i  functions: Beginning execution of "us-central1-apis"
i  functions: Finished "us-central1-apis" in ~7s
i  functions: Beginning execution of "us-central1-apis"
i  functions: Finished "us-central1-apis" in ~7s
i  functions: Beginning execution of "us-central1-apis"
i  functions: Finished "us-central1-apis" in ~6s
i  functions: Beginning execution of "us-central1-apis"
i  functions: Finished "us-central1-apis" in ~7s
i  functions: Beginning execution of "us-central1-apis"
i  functions: Finished "us-central1-apis" in ~7s

优化方案

1. 并行化Firestore查询

当前示例接口中4个Firestore查询是串行执行的,总耗时为各查询耗时之和。改成并行执行可大幅减少总耗时:

app.get("/test-home", async (req, res) => {
  try {
    // 并行发起所有查询
    const [popularSnapshot, hotSnapshot, deptsSnapshot, topDeptsSnapshot] = await Promise.all([
      db.collection("products")
        .where("status", "==", "active")
        .orderBy("visited", "desc")
        .limit(20)
        .get(),
      db.collection("products")
        .where("status", "==", "active")
        .orderBy("todaysSales", "desc")
        .limit(20)
        .get(),
      db.collection("departments").get(),
      db.collection("departments")
        .orderBy("products_sold_today", "desc")
        .limit(6)
        .get()
    ]);

    // 处理查询结果
    const content = {
      popular_today: popularSnapshot.docs.map(x => product_basic_obj(x.data())),
      hot_today: hotSnapshot.docs.map(x => product_basic_obj(x.data())),
      departments: deptsSnapshot.docs.map(x => x.data()),
      top_departments: topDeptsSnapshot.docs.map(x => x.data())
    };

    return res.status(200).json(content);
  } catch (err) {
    return res.status(500).json({ error: err.message });
  }
});

2. 检查Firestore索引与查询效率

  • 确保所有带where+orderBy的查询都有复合索引,Firebase控制台会在查询失败时提示创建索引,未创建索引会导致全表扫描,大幅增加耗时。
  • 避免db.collection("departments").get()这类全集合查询,如果部门数据量大,考虑分页或只查询必要字段(用select()方法)。

3. 升级依赖版本

当前使用的firebase-admin(8.6.0)、firebase-functions(3.23.0)版本较旧,新版本通常包含性能优化和bug修复:

  • 升级firebase-admin到最新稳定版
  • 升级firebase-functions到最新稳定版
  • 同步升级其他相关依赖(如firebase)

4. 启用Cloud Functions的CPU/内存升级

默认的Cloud Functions资源配置(256MB内存)可能不足以应对高负载,可在函数配置中提升资源:

exports.apis = functions.runWith({
  memory: "1GB", // 可选 512MB/1GB/2GB等
  timeoutSeconds: 30
}).https.onRequest(app);

5. 缓存高频查询结果

对于首页这类高频访问的接口,可将查询结果缓存到内存缓存或Firebase Cache,缓存有效期设为5-15分钟,避免重复查询Firestore:

const cache = require('memory-cache');

app.get("/test-home", async (req, res) => {
  const cacheKey = "home_page_content";
  const cachedContent = cache.get(cacheKey);
  
  if (cachedContent) {
    return res.status(200).json(cachedContent);
  }

  // 执行并行查询(同方案1)
  const [popularSnapshot, hotSnapshot, deptsSnapshot, topDeptsSnapshot] = await Promise.all([...]);
  const content = { ... };

  cache.put(cacheKey, content, 10 * 60 * 1000); // 缓存10分钟
  return res.status(200).json(content);
});

6. 移除不必要的中间件与依赖

检查package.json中的依赖,移除未使用的库(如easy-soap-request、xml-js如果当前接口未用到),减少函数启动时的加载时间。同时,确保Express中间件只在需要的路由上使用,而非全局挂载。

7. 使用Firestore的select()减少数据传输

查询时只获取需要的字段,避免传输冗余数据:

db.collection("products")
  .where("status", "==", "active")
  .orderBy("visited", "desc")
  .limit(20)
  .select("id", "name", "price", "image") // 只获取必要字段
  .get()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 07:55:20