在获取数据时,还原缓慢的性能问题

与1/10比率的实时数据库相比,我在检索文档中存储的基本数据时遇到了火还原的性能问题。

使用 Firest,第一次调用平均需要3000毫秒

 this.db.collection(‘testCol’)
.doc(‘testDoc’)
.valueChanges().forEach((data) => {
console.log(data);//3000 ms later
});

使用实时数据库,第一次呼叫平均需要300ms

 this.db.database.ref(‘/test’).once(‘value’).then(data => {
console.log(data); //300ms later
});

这是网络控制台的屏幕截图:

Firestore slow performance issue get Data

我使用 AngularFire2 v5.0 rc. 2运行 Javascript SDK v4.50。

有人经历过这个问题吗?

42846 次浏览

更新: 2018年2月12日-iOS Firest SDK v0.10.0

与其他一些评论者类似,我还注意到第一个 get 请求的响应速度较慢(后续请求大约需要100ms)。对于我来说,它没有30岁时那么糟糕,但是当我有良好的连接性时,可能会有2-3岁左右,这足以在我的应用程序启动时提供糟糕的用户体验。

Firebase 表示他们已经意识到这个“冷启动”问题,并且正在为此进行长期修复——不幸的是,预计到达时间还没有。我认为这是一个单独的问题,当我连接性差时,可能需要很长时间(超过30年)才能让请求决定从缓存中读取。

虽然 Firebase 修复了所有这些问题,但是我已经开始使用新的 disableNetwork()enableNetwork()方法(可以在 Firestorv0.10.0中获得)来手动控制 Firebase 的在线/离线状态。尽管我在代码中使用 非常的时候必须非常小心,因为在某些情况下,有一个 Firestorbug 会导致崩溃。


更新: 2017年11月15日-iOS FirestorSDK v0.9.2

现在看来,缓慢的性能问题已经得到解决。我已经重新运行了下面所描述的测试,现在看来,Firest 返回100个文档所需的时间一直在100毫秒左右。

不确定这是否是最新的 SDK v0.9.2中的补丁,或者是后端补丁(或者两者兼而有之) ,但我建议每个人都更新他们的 Firebase pods。我的应用程序的响应速度明显更快——类似于它在 Realtime DB 上的响应速度。


我还发现,火还原比实时数据库慢得多,特别是在读取大量文档时。

更新的测试(使用最新的 iOS FirestorSDK v0.9.0) :

我在 iOS Swift 中设置了一个测试项目,同时使用了 RTDB 和 Firest,并对每个测试项目运行了100个顺序读取操作。对于 RTDB,我在100个顶级节点中的每个节点上测试了观察 SingleEvent 和观察方法。对于火灾恢复,我在 TestCol 集合中的100个文档中的每个文档中都使用了 getDocument 和 addSnapshoListener 方法。我断断续续地运行磁盘持久性测试。请参阅所附的图片,其中显示了每个数据库的数据结构。

我在同一台设备和一个稳定的 wifi 网络上对每个数据库进行了10次测试。现有的观察器和侦听器在每次新运行之前被销毁。

实时数据库观察 SingleEvent 方法:

func rtdbObserveSingle() {


let start = UInt64(floor(Date().timeIntervalSince1970 * 1000))
print("Started reading from RTDB at: \(start)")


for i in 1...100 {
Database.database().reference().child(String(i)).observeSingleEvent(of: .value) { snapshot in
let time = UInt64(floor(Date().timeIntervalSince1970 * 1000))
let data = snapshot.value as? [String: String] ?? [:]
print("Data: \(data). Returned at: \(time)")
}
}
}

实时数据库观察方法:

func rtdbObserve() {


let start = UInt64(floor(Date().timeIntervalSince1970 * 1000))
print("Started reading from RTDB at: \(start)")


for i in 1...100 {
Database.database().reference().child(String(i)).observe(.value) { snapshot in
let time = UInt64(floor(Date().timeIntervalSince1970 * 1000))
let data = snapshot.value as? [String: String] ?? [:]
print("Data: \(data). Returned at: \(time)")
}
}
}

返回 getDocument 方法:

func fsGetDocument() {


let start = UInt64(floor(Date().timeIntervalSince1970 * 1000))
print("Started reading from FS at: \(start)")


for i in 1...100 {
Firestore.firestore().collection("TestCol").document(String(i)).getDocument() { document, error in


let time = UInt64(floor(Date().timeIntervalSince1970 * 1000))
guard let document = document, document.exists && error == nil else {
print("Error: \(error?.localizedDescription ?? "nil"). Returned at: \(time)")
return
}
let data = document.data() as? [String: String] ?? [:]
print("Data: \(data). Returned at: \(time)")
}
}
}

火恢复 addSnapshoListener 方法:

func fsAddSnapshotListener() {


let start = UInt64(floor(Date().timeIntervalSince1970 * 1000))
print("Started reading from FS at: \(start)")


for i in 1...100 {
Firestore.firestore().collection("TestCol").document(String(i)).addSnapshotListener() { document, error in


let time = UInt64(floor(Date().timeIntervalSince1970 * 1000))
guard let document = document, document.exists && error == nil else {
print("Error: \(error?.localizedDescription ?? "nil"). Returned at: \(time)")
return
}
let data = document.data() as? [String: String] ?? [:]
print("Data: \(data). Returned at: \(time)")
}
}
}

每个方法在开始执行时以毫秒为单位打印 unix 时间戳,然后在每个 read 操作返回时打印另一个 unix 时间戳。我获取了初始时间戳和最后一个时间戳之间的差值来返回。

结果-磁盘持久性失效:

Disk persistence disabled

结果-启用磁盘持久性:

Disk persistence enabled

数据结构:

Data Structure

当 FirestorgetDocument/addSnapshoListener 方法被卡住时,它似乎被卡住的持续时间大约是30秒的几倍。也许这可以帮助 Firebase 团队分离出 SDK 中卡住的地方?

今天早上之前我一直有这个问题。通过 iOS/Swift 完成一个简单的、完全索引的查询需要大约20秒钟,返回1个项目的查询时间不成比例,最多可达3,000。

我的解决方案是禁用脱机数据持久性。在我的案例中,它并不适合我们的数据库的需要,因为我们的数据库每天都有大量的数据更新。

IOS 和 Android 用户默认启用这个选项,而 web 用户默认禁用这个选项。如果您要查询大量的文档集合,那么它会使 Firest 看起来非常缓慢。基本上,它会缓存你正在查询的任何数据的副本(以及你正在查询的任何集合——我相信它会缓存里面的所有文档) ,这会导致高内存使用率。

在我的例子中,它导致每个查询都要等待很长时间,直到设备缓存了所需的数据——因此,从完全相同的集合返回的条目数量越来越多,所需的查询时间是不成比例的。这是因为在每个查询中缓存集合所需的时间相同。

离线数据-来自云消防文档

我执行了一些基准测试来从相同的查询集合显示这种效果(启用了离线持久性) ,但是使用。限制参数:

Benchmarks 现在返回了100个项目(禁用了脱机持久性) ,我的查询只需不到1秒钟就可以完成。

我的火还原查询代码如下:

let db = Firestore.firestore()
self.date = Date()
let ref = db.collection("collection").whereField("Int", isEqualTo: SomeInt).order(by: "AnotherInt", descending: true).limit(to: 100)
ref.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let data = document.data()
//Do things
}
print("QUERY DONE")
let currentTime = Date()
let components = Calendar.current.dateComponents([.second], from: self.date, to: currentTime)
let seconds = components.second!
print("Elapsed time for Firestore query -> \(seconds)s")
// Benchmark result
}
}

根据我目前在模拟器和真正的安卓手机华为 p8中使用 nexus 5 x 所做的研究,

火还原和云存储都让我头疼,因为它们的响应速度太慢了 当我执行第一个 document.get ()和第一个 storage.getDownloadUrl ()时

它给我超过60秒的响应每个请求。缓慢的响应只发生在真正的 Android 手机。不是在模拟器里。另一件奇怪的事。 在第一次遭遇之后,休息请求是顺利的。

下面是我遇到响应慢的简单代码。

var dbuserref = dbFireStore.collection('user').where('email','==',email);
const querySnapshot = await dbuserref.get();


var url = await defaultStorage.ref(document.data().image_path).getDownloadURL();

我还发现了链接,正在研究相同的。 Https://reformatcode.com/code/android/firestore-document-get-performance

更新日期: 2018年3月2日

看起来这是个众所周知的问题,而且火灾恢复公司的工程师们正在修复这个问题。经过几次电子邮件交流和代码共享与一个 Firestore 工程师在这个问题上,这是他今天的回应。

“你说得对。经过进一步检查,getDocument () API 上的这种缓慢是 CloudFirestorbeta 中已知的行为。我们的工程师已经意识到这个被标记为“冷启动”的性能问题,但是不要担心,因为我们正在尽最大努力来提高火还原查询的性能。

我们已经着手于一个长期的解决方案,但我现在不能透露任何时间表或具体细节。尽管 Firest 仍处于测试阶段,但预计未来还会有更多改进。”

所以希望这个能很快被淘汰。


使用 Swift/iOS

经过3天的处理,这个问题似乎是肯定的得到()即。获取文档和。GetDocument.事情 I 思考造成了极端的间歇性延迟,但似乎并非如此:

  1. 网络连接不是很好
  2. 通过循环 over. getDocument ()重复调用
  3. 链接接听电话
  4. 冷启动
  5. 获取多个文档(获取1个小文档会导致20秒的延迟)
  6. 缓存(我禁用了离线持久性,但这没有任何作用。)

我能够排除所有这些,因为我注意到这个问题并没有发生在每一个我正在进行的数据库调用。只使用 get ()进行检索。为了刺激,我替换了。的 getDocument。AddSnapshoListener 检索我的数据,瞧。每次包括第一次调用的即时检索。没有冰冷的开始。到目前为止没有问题。AddSnapshoListener,只有 getDocument (s)。

现在,我只是简单地删除了. getDocument () ,其中时间是最重要的,然后用. addSnapshoListener 替换它,然后使用

for document in querySnapshot!.documents{
// do some magical unicorn stuff here with my document.data()
}

... 以便继续前进,直到这得到解决,由火灾恢复。

差不多3年后,firest 已经完全脱离了 beta 测试,我可以确认这个可怕的问题仍然存在;

在我们的移动应用程序中,我们使用 javascript/node.js firebase 客户端。经过大量的测试,找出为什么我们的应用程序的启动时间在10秒左右,我们确定了70% 的时间应该归因于... ... 嗯,归因于 firebase 和 firestore 的性能和冷启动问题:

  • Auth () . onAuthStateChanged ()在1.5 -2秒后触发,已经相当糟糕了。
  • 如果它返回一个用户,我们将使用它的 ID 从 firestore 获取用户文档。这是第一次调用 firestore,相应的 get ()需要4-5秒。相同或其他文档的后续 get ()大约需要500毫秒。

因此,总的来说,用户初始化需要6-7秒,完全不可接受。我们无能为力。我们不能测试禁用持久性,因为在 javascript 客户机中没有这样的选项,默认情况下持久性总是启用的,所以不调用 ablePerence ()不会改变任何东西。