【问题标题】:Firebase Android offline performanceFirebase Android 离线性能
【发布时间】:2016-06-12 17:19:11
【问题描述】:

当在单个节点下存储大约 5000 个子节点时,在使用离线功能时初始化 firebase 会变得非常慢。在执行第一个查询之前大约需要 30 秒。初始化后,执行后续查询(例如列出前 25 个子节点)只需不到一秒的时间。

我正在使用以下属性来启用离线功能: Firebase.getDefaultConfig().setPersistenceEnabled(true); firebase.keepSynced(true);

我的结构是这样的:

<root>
 |-my-app-name
   |-<uid>
     |-node
       |-sub node 1
       |-...
       |-sub node 5000

&lt;uid&gt; 节点上设置保持同步。子节点显示在 Recycler 视图中。最好是列出所有(而不是每页 25 个),但我知道这是不可能的,因为没有类似光标的机制(如 Android 为 SQLite 提供的那样)可用于使用 Firebase。

这是设计使然,我是否要修改我的数据结构?或者我可以通过其他方式减少初始化时间吗?

我在下面提供了一些日志记录。如您所见,正在进行大量垃圾收集。 Firebase 是否在初始化时评估整个数据库?

谢谢! 尼尔斯

04-01 15:59:12.029 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 43005(1717KB) AllocSpace objects, 0(0B) LOS objects, 4% free, 31MB/32MB, paused 5.674ms total 57.402ms
04-01 15:59:13.415 2222-2240/abcdef W/art: Suspending all threads took: 6.600ms
04-01 15:59:13.424 2222-2245/abcdef W/art: Suspending all threads took: 9.339ms
04-01 15:59:13.433 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 7097(281KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 32MB/32MB, paused 11.175ms total 27.105ms
04-01 15:59:13.821 2222-2245/abcdef I/art: Background partial concurrent mark sweep GC freed 101674(5MB) AllocSpace objects, 18(530KB) LOS objects, 35% free, 28MB/44MB, paused 3.400ms total 152.664ms
04-01 15:59:15.107 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 394024(15MB) AllocSpace objects, 0(0B) LOS objects, 20% free, 30MB/38MB, paused 1.865ms total 152.182ms
04-01 15:59:15.817 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 218328(8MB) AllocSpace objects, 0(0B) LOS objects, 19% free, 31MB/38MB, paused 1.711ms total 112.325ms
04-01 15:59:16.451 2222-2240/abcdef W/art: Suspending all threads took: 27.786ms
04-01 15:59:16.465 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 190591(7MB) AllocSpace objects, 0(0B) LOS objects, 18% free, 31MB/38MB, paused 1.832ms total 107.416ms
04-01 15:59:16.472 2222-2245/abcdef W/art: Suspending all threads took: 6.823ms
04-01 15:59:17.084 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 178714(6MB) AllocSpace objects, 0(0B) LOS objects, 15% free, 32MB/38MB, paused 1.717ms total 105.529ms
04-01 15:59:17.629 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 163584(6MB) AllocSpace objects, 0(0B) LOS objects, 14% free, 33MB/38MB, paused 1.743ms total 110.764ms
04-01 15:59:18.941 2222-2240/abcdef W/art: Suspending all threads took: 5.078ms
04-01 15:59:19.691 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 95627(3MB) AllocSpace objects, 0(0B) LOS objects, 8% free, 35MB/38MB, paused 7.190ms total 86.171ms
04-01 15:59:19.961 2222-2240/abcdef W/art: Suspending all threads took: 18.208ms
04-01 15:59:20.965 2222-2245/abcdef W/art: Suspending all threads took: 5.254ms
04-01 15:59:20.990 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 55899(2MB) AllocSpace objects, 0(0B) LOS objects, 5% free, 36MB/38MB, paused 6.799ms total 66.923ms
04-01 15:59:22.495 2222-2240/abcdef W/art: Suspending all threads took: 45.180ms
04-01 15:59:22.509 2222-2245/abcdef W/art: Suspending all threads took: 14.254ms
04-01 15:59:22.562 2222-2245/abcdef I/art: Background partial concurrent mark sweep GC freed 198174(6MB) AllocSpace objects, 3(487KB) LOS objects, 32% free, 33MB/49MB, paused 16.949ms total 215.369ms
04-01 15:59:23.811 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 392437(15MB) AllocSpace objects, 0(0B) LOS objects, 18% free, 35MB/43MB, paused 1.936ms total 168.222ms
04-01 15:59:24.480 2222-2240/abcdef W/art: Suspending all threads took: 22.464ms
04-01 15:59:24.497 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 227043(8MB) AllocSpace objects, 0(0B) LOS objects, 18% free, 35MB/43MB, paused 1.723ms total 117.855ms
04-01 15:59:25.173 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 203910(7MB) AllocSpace objects, 0(0B) LOS objects, 16% free, 36MB/43MB, paused 1.694ms total 112.618ms
04-01 15:59:25.181 2222-2245/abcdef W/art: Suspending all threads took: 7.301ms
04-01 15:59:25.784 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 185627(7MB) AllocSpace objects, 0(0B) LOS objects, 14% free, 37MB/43MB, paused 1.719ms total 115.362ms
04-01 15:59:26.345 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 167066(6MB) AllocSpace objects, 0(0B) LOS objects, 13% free, 37MB/43MB, paused 1.651ms total 106.055ms
04-01 15:59:26.865 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 154535(6MB) AllocSpace objects, 0(0B) LOS objects, 11% free, 38MB/43MB, paused 1.644ms total 104.888ms
04-01 15:59:28.357 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 151375(5MB) AllocSpace objects, 33(671KB) LOS objects, 9% free, 39MB/43MB, paused 2.740ms total 104.176ms
04-01 15:59:29.006 2222-2240/abcdef W/art: Suspending all threads took: 19.232ms
04-01 15:59:29.060 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 133554(5MB) AllocSpace objects, 29(580KB) LOS objects, 10% free, 39MB/43MB, paused 1.563ms total 100.220ms
04-01 15:59:30.173 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 131062(4MB) AllocSpace objects, 31(637KB) LOS objects, 9% free, 39MB/43MB, paused 1.653ms total 102.705ms
04-01 15:59:31.245 2222-2245/abcdef I/art: Background sticky concurrent mark sweep GC freed 122085(4MB) AllocSpace objects, 26(522KB) LOS objects, 8% free, 39MB/43MB, paused 2.380ms total 100.776ms
04-01 15:59:32.024 2222-2240/abcdef W/art: Suspending all threads took: 20.662ms

PS:这是一个交叉帖:https://groups.google.com/forum/#!topic/firebase-talk/migEAwv26ns

【问题讨论】:

  • 从磁盘上的数据构建内部数据模型需要时间。虽然该过程可能会在 Firebase SDK 中进行优化,但您无法控制它。您拥有的唯一控制是缓存更少的数据。
  • 将 .keepSynced(true) 设置为多个“子”路径而不是单个“根”路径是否有帮助?例如。 .keepSynced(true), .keepSynced(true) 等。而不是 .keepSynced(true)?换句话说,数据模型是整体读取还是延迟读取各个 Firebase 路径(例如,当第一次访问路径时)?
  • @Niels 你找到解决办法了吗?
  • @VonD 我正在尝试对数据进行分片,因此一个根节点最多包含 200 个子节点。我在分片上设置 .keepSynced(true) ,第一印象是性能要好得多。但由于这是一个周日下午的项目,我还没有时间来验证我的所有假设。一旦我有,我会发布一个明确的答案。
  • @Niels 好的,非常感谢

标签: android firebase


【解决方案1】:

初始化意味着setValue 到那个节点,对吧?因此,在一个节点下初始化 5000 个子节点大约需要 30 秒,这对我来说似乎很不寻常。我在单个节点下处理了几乎相同大小的数据,性能要好得多。所以我不确定你在单个子节点下放置了多少属性,但无论如何,我想你需要再次检查性能。我认为您在setValue 上使用onCompleteListener 来计算初始化数据所花费的时间,因为UI 视图没有为您提供确切的时间并且通常比实际操作时间慢。

最好我想列出所有(而不是每页 25 个),但我 明白这是不可能的,因为没有像 Cursor 这样的 可用于使用的机制(如 Android 为 SQLite 提供的那样) 火力基地。

虽然我不太确定您的目的,但对于这些类型的情况,我可以建议同时维护 Sqlite 和 Firebase 数据库。让我澄清一下。

这个想法是为用户手机中的特定用户维护 Firebase 数据库的相同副本。这样本地数据库就可以在需要时充分满足您的目的。您可以查询数据库并可以使用CursorLoader,您已经掌握了一些经验。

它还有其他一些优点。您可以使用自己的机制处理离线同步。当互联网中断时,将您想要同步的数据存储在本地 Sqlite 数据库中,然后当连接建立时,您将在 BroadcastReceiver 中收到回调。然后您可以轻松地将离线数据setValue 发送到 Firebase。 Firebase 让这一切变得更简单,但无论如何,由于您非常关心性能,您可以尝试一下。

当您的应用程序做太多工作时,您发布的 GC 行为很常见。 Firebase 基本上使用WebSocket 来维护与远程数据库的连接。因此,我认为您需要检查是否保持与 ​​Firebase 数据库的不必要连接。当不再需要监听器时,尝试使用removeListener

Firebase 在初始化时是否评估整个数据库?

我还不确定您的初始化是什么意思,但是是的,如果您再次将 setValue 的同一节点带到该节点,它将用新的数据集替换以前的数据。

【讨论】:

  • > 初始化意味着该节点的 setValue 对吗?不,在 Firebase.setContext() 和 Firebase.getDefaultConfig().setPersistenceEnabled(true) 中初始化 firebase。我在我的 Android 应用程序子类 onCreate() 方法中执行这些操作。为了澄清,已经创建了 5000 个节点。当我想列出这些节点时(例如在 RecyclerView 中),我看到在执行实际查询之前有很长的初始延迟(约 30 秒)。
  • > Firebase 基本上使用 WebSocket 来维护与远程数据库的连接。
【解决方案2】:

因此,将数据分片以使一个根节点最多包含 200 个子节点似乎是目前的答案。我在分片上设置了 .keepSynced(true),这会带来更好的性能。

为了在单个回收器视图中显示分片列表,我创建了一个 FirebaseArrays 类,它是 FirebaseArray 的集合,它将多个数组聚合到一个可观察的集合中。
https://gist.github.com/ndefeijter/2191f8a43ce903c5d9ea69f02c7ee7e9

我还调整了 FirebaseRecyclerAdapter 以使用 FirebaseArrays 作为底层数据结构,而不是单个 FirebaseArray。该接口使用一些方法进行了扩展,以添加额外的 Firebase 路径(即分片)。
https://gist.github.com/ndefeijter/d98eb5f643b5faf5476b8a611de912c1

这些路径是在“加载更多”事件时添加的(例如,在无限滚动的情况下)。

private void loadMore() {

    final View view = getView();
    if (null != view) {
        final RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
        final FirebaseRecyclerAdapter2<Visit, VisitViewHolder> adapter = (FirebaseRecyclerAdapter2<Visit, VisitViewHolder>) recyclerView.getAdapter();
        adapter.addQuery(nextQuery());
    }
}

【讨论】:

    猜你喜欢
    • 2016-06-30
    • 2021-11-25
    • 2016-04-01
    • 1970-01-01
    相关资源
    最近更新 更多