【问题标题】:Why is my read query to Firebase Realtime Database so slow?为什么我对 Firebase 实时数据库的读取查询如此缓慢?
【发布时间】:2021-11-28 09:15:07
【问题描述】:

我在 Firebase 实时数据库中有一个数据库,其数据如下所示:

root
|_history
  |_{userId}
    |_{n1}
    | |_ ...
    |_{n2}
    |_{n...}

节点n 以日期整数值作为键。每个n 节点至少有 60 个键,其中一些值是数组,最大深度为 5 级。

查询时间的测量方式与此类似:

const startTime = performance.now();

await query();

const endTime = performance.now();

logger.info(`Query completed in ${endTime - startTime} ms`);

我有一个函数可以查询history/${userId} 下的n 节点,其中的键介于开始值和结束值之间(包括开始值和结束值):

await admin
  .database()
  .ref(`history/${userId}`)
  .orderByKey()
  .startAt(`${start}`)
  .endAt(`${end}`)
  .once("value")

此查询在可调用的云函数中执行。此查询当前大约需要 2-3 秒,返回大约 225 个节点。 n 节点总数目前不到 300 个。查看我的日志,返回 0 个节点的查询时间看起来大约需要 500 毫秒。

为什么查询这么慢?我对 Firebase 的实时数据库有什么误解吗?

【问题讨论】:

  • 代码乍一看很好,所以只是一些故障排除问题/cmets:1)索引似乎与您运行的查询无关,2)您要检索多少数据?你在哪里运行这段代码?那里的带宽是多少? 3) 当您检索较少的数据时,性能如何影响性能?要么通过缩小startend 之间的范围,要么通过减少每个节点的数据量?
  • 我已经用更多信息更新了问题正文,这些信息至少应该部分涵盖您评论中的问题。让我知道添加的信息是否足够,或者您是否仍需要更多详细信息。
  • 既然你说你在Cloud Functions中运行这个,你能不能用本地的Node.js脚本测试一下,看看性能是否一样?如果不是,那么您问题中的代码可能与性能无关。
  • 快速测试平台与 JS SDK 进行对比:jsbin.com/lefosid/6/edit?js,console
  • 感谢您为此付出的努力!我重组了一些数据。这已经解决了我上次编辑中第一个查询的问题,我已经将它降低到 10 毫秒以下。我已经更新了问题,删除了关于第一个查询的信息。

标签: node.js firebase-realtime-database firebase-admin


【解决方案1】:

我进行了一些性能测试,以便您进行比较。

我用这个脚本填充了我的数据库:

for (var i=0; i < 500; i++) {
  ref.push({
    refresh_at: Date.now() + Math.round(Math.random() * 60 * 1000)
  });
}

这导致了这种形式的 JSON:

{
  "-MlWgH51ia7Iz7ubZb7K" : {
    "refresh_at" : 1633726623247
  },
  "-MlWgH534FgMlb7J4bH2" : {
    "refresh_at" : 1633726586126
  },
  "-MlWgH54gd-uW_M7e6J-" : {
    "refresh_at" : 1633726597651
  },
  ...
}

当通过 API 完整检索时,此 JSON 的 snapshot.val() 长度为 26.001 个字符。


jsbin 中的客户端 JavaScript SDK

使用jsbin 中的常规客户端 JavaScript SDK 和类似于您的简单节点脚本。

针对jsbin更新,我跑的代码是:

ref.orderByChild("refresh_at")
  .endAt(Date.now())
  .limitToLast(1000) // ? This is what we'll vary
  .once("value")
  .then(function(snapshot) {
  var endTime = performance.now();
  console.log('Query completed in '+Math.round(endTime - startTime)+'ms, retrieved '+snapshot.numChildren()+" nodes, for a total JSON size of "+JSON.stringify(snapshot.val()).length+" chars");  
});

运行几次,然后更改我标记的limit,会导致:

Limit Snapshot size Average time in ms
500 26,001 350ms - 420ms
100 5,201 300ms - 350ms
10 521 300ms - 320ms

Node.js 管理 SDK

我使用本地 Node.js 脚本针对完全相同的数据集运行了相同的测试,修改后的脚本运行了 10 次:

for (var i=0; i < 10; i++) {
  const startTime = Date.now();
  const snapshot = await ref.orderByChild("refresh_at")
    .endAt(Date.now())
    .limitToLast(10)
    .once("value")
  const endTime = Date.now();
  console.log('Query completed in '+Math.round(endTime - startTime)+'ms, retrieved '+snapshot.numChildren()+" nodes, for a total JSON size of "+JSON.stringify(snapshot.val()).length+" chars");  
};

结果:

Limit Snapshot size Time in ms
500 26,001 507ms, 78ms, 70ms, 65ms, 65ms, 61ms, 64ms, 65ms, 81ms, 62ms
100 5,201 442ms, 59ms, 56ms, 59ms, 55ms, 54ms, 54ms, 55ms, 57ms, 56ms
10 521 437ms, 52ms, 49ms, 52ms, 51ms, 51ms, 52ms, 50ms, 52ms, 50ms

因此您可以看到,第一次运行与 JavaScript SDK 相似(但稍慢),随后的运行速度要快得多。这是有道理的,因为在初始运行时,客户端会建立与数据库服务器的(Web 套接字)连接,其中包括几次往返以确定正确的服务器。后续调用似乎更受带宽限制。


按键排序

我还在 Node.js 中使用ref.orderByKey().startAt("-MlWgH5QUkP5pbQIkVm0").endAt("-MlWgH5Rv5ij42Vel5Sm") 进行了测试,得到的结果与按子排序非常相似。

【讨论】:

    猜你喜欢
    • 2011-02-21
    • 1970-01-01
    • 2017-09-19
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多