【问题标题】:Firebase Database: Out of Memory Exception on querying huge amount of data on Android appFirebase 数据库:在 Android 应用上查询大量数据时出现内存不足异常
【发布时间】:2018-07-03 20:07:34
【问题描述】:

我正在为我的 Android 应用程序使用 Firebase 数据库。我需要一个有效期为 3 天的唯一 5 位数代码。为了解决这个问题,我在 Firebase 数据库中插入了所有可能的 5 位代码,其中每个条目最初的状态为 1。所以结构看起来像这样:

codes: {
  'abcde': {status:1},
  'zhghs': {status:1}
  .....
}

这个codes 对象有近500 万个条目。我在想一旦代码在使用中,我会将状态设置为2

我正在使用它从 Firebase 获取代码:

db.getReference("codes")
                .orderByChild("status")
                .limitToFirst(1)
                .addListenerForSingleValueEvent { ..... }

我只需要 1 个状态为 1 的代码。上面的代码导致OutOfMemoryException 所以我认为它正在尝试加载设备上的所有数据。由于limitToFirst(1) 约束,它不应该只在设备端加载一个元素吗?

附:我已经尝试过使用 Firebase FireStore。截至目前,它不允许在不超过每日 20K 限制的情况下导入大量 JSON 数据。

编辑 - 我什至设置了db.setPersistenceEnabled(false) 确切的错误是:

Firebase 数据库遇到 OutOfMemoryError。你可能需要 减少同步到客户端的数据量。 java.lang.OutOfMemoryError: 分配 28701016 字节失败 分配 16777216 个可用字节和 18MB 直到 OOM

【问题讨论】:

  • 请编辑您的问题,将您的描述限制在一个问题上。一切都很合理,直到最后你开始列出一堆其他问题,这些问题不适合 Stack Overflow。
  • @DougStevenson 删除了他们
  • 所以,为了确定,如果您在您的应用程序中除了这个查询之外什么都不做,您会遇到同样的问题吗?
  • @DougStevenson 在我点击此查询之前,我的应用程序已完全加载。确切的错误是Firebase Database encountered an OutOfMemoryError. You may need to reduce the amount of data you are syncing to the client. java.lang.OutOfMemoryError: Failed to allocate a 28701016 byte allocation with 16777216 free bytes and 18MB until OOM
  • 问题不在于您的应用完全加载。 (也许它加载太多,谁知道呢?)问题是发现查询实际上占用了太多内存而没有应用程序其余部分的上下文。

标签: java android firebase firebase-realtime-database


【解决方案1】:

听起来您可能没有在 status 属性上定义索引。在这种情况下,服务器无法为您进行排序/过滤,它将在客户端中完成。这意味着客户端必须读取所有数据并将其保存在内存中,这说明了巨大的内存使用量。

解决方案非常简单:在您的 status 属性上定义一个索引。

{
  "rules": {
    "codes": {
      ".indexOn": "status"
    }
  }
}

另见documentation on indexing data

【讨论】:

  • 工作就像一个魅力。我实际上计划添加此规则,但我认为我在开发中不需要它,因为大多数情况下所有状态代码都是相同的。非常感谢。
  • 拥有 500 万个条目,您肯定需要一个索引。哎呀,有了这么多的条目,我仍然会考虑创建您自己的反向索引,您可以在其中从您自己的代码创建状态为 1 的所有项目的列表,例如/itemsByStatus/1/itemId1: true。这可以更好地扩展,从那时起您可以订购/限制项目,以获得一个子集:ref.child("itemsByStatus/1").limitToFirst(10)
【解决方案2】:

我只需要 1 个状态为 1 的代码。

在这种情况下,将您的查询更改为:

db.getReference("codes")
            .orderByChild("status")
            .equalsTo(1)
            .limitToFirst(1)
            .addListenerForSingleValueEvent { ..... }

即使有这个限制,您也应该减少加载到内存中的数据量,因为您似乎将太多数据强制到内存中。或者,您可以通过在 Manifest.xml 文件中设置 android:largeHeap="true" 来设置较大的堆大小。通常这个错误会消失。

你也可以看看这篇密切相关的文章here中的讨论。

正如@DougStevenson 所说,这并不是解决OOM 错误的真正推荐方法,但正如我在@FrankvanPuffelen 的回答中看到的那样,解决问题的关键是添加最初缺少的".indexOn": "status"

【讨论】:

  • 最初所有代码的状态 = 1,因此添加 equalsTo(1) 不会减少数据集。使用android:largeHeap="true" 对我来说不是正确的选择,因为我最终将转向 6 位代码,其中数据会更多,我不想为一个代码在 RAM 中加载数百 MB 的数据。
  • 使用 largeHeap 并不是真正推荐的解决 OOM 错误的方法,因为设备之间仍然存在巨大的内存差异,而且 Android 绝不有义务为您提供比它选择的更多的内存.正确的解决方案是使用更少的内存。 developer.android.com/guide/topics/manifest/…
猜你喜欢
  • 2023-04-10
  • 2012-10-23
  • 2013-10-09
  • 2018-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-02
相关资源
最近更新 更多