【问题标题】:firebase query performance orderByChild + .indexOn VS orderByKeyfirebase 查询性能 orderByChild + .indexOn VS orderByKey
【发布时间】:2017-11-21 23:22:14
【问题描述】:

我在这里面临一个非常奇怪的决定,即关于处理 Firebase 数据库的以下场景的性能,我正在做的是我生成一个随机的 customerId 以供替代使用并将其存储在该内部配置文件中(我仍然使用 Firebase uid,但它只是用于客户想要的“友好数字”)。

我正在尝试执行以下操作之一:

当我收到请求时:

UserVO createdUser = Json.fromJson(getRequestBodyAsJson(), UserVO.class);
CompletableFuture<String> checkCustomerIdCompletableFuture = firebaseDatabaseService.buildUniqueCustomerId();
return checkCustomerIdCompletableFuture.thenApply(customerId -> {
    createdUser.setCustomerId(customerId);
    return firebaseDatabaseService.addToUserProfile(createdUser.getId(), getObjectAsMapOfObjects(createdUser));
}).thenCompose(completableFuture -> CompletableFuture.completedFuture(ok(Json.toJson(createdUser))));

customerId 始终在配置文件中编入索引:

"profiles":{
   "$uid":{
      ".read":"$uid === auth.uid",
      ".write":"$uid === auth.uid",
      },
    ".indexOn": ["customerId", "email"]
 }

在这两种情况下,用户的个人资料应该是这样的:

"profiles" : {
  "jiac4QpEfggRTuKuTfVOisRGFJn1" : {
    "contactPhone" : "",
    "createdAt" : 1499606268255,
    "customerId" : 4998721187, // OR "A-4998721187" as string
    "email" : "almothafar@example.com",
    "firstName" : "Al-Mothafar",
    "fullName" : "Al-Mothafar Al-Hasan",
    "id" : "jiac4QpEfggRTuKuTfVOisRGFJn1",
    "lastName" : "Al-Hasan2",
    "updatedAt" : 1499857345960,
    "verified" : false
  }
}

buildUniqueCustomerId() 这里有 2 个选项:

第一个是直接在profiles内部查询customerId,并返回唯一id,使用queryByChildcustomerId被索引:

    public CompletableFuture<String> buildUniqueCustomerId() {
    String customerId = String.valueOf(System.currentTimeMillis()).substring(1, 9).concat(RandomStringUtils.randomNumeric(2));

    CompletableFuture<String> dataSnapshotCompletableFuture = new CompletableFuture<>();
    firebaseDatabaseProvider.getUserDataReference().child("/profiles").orderByChild("customerId").equalTo(customerId).limitToFirst(1)
            .addChildEventListener(new ChildEventListener() {
                @Override
                public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
                    if (snapshot.exists()) {
                        buildUniqueCustomerId();
                    } else {
                        dataSnapshotCompletableFuture.complete(customerId);
                    }
                }

                @Override
                public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
                    if (snapshot.exists()) {
                        buildUniqueCustomerId();
                    } else {
                        dataSnapshotCompletableFuture.complete(customerId);
                    }
                }

                @Override
                public void onChildRemoved(DataSnapshot snapshot) {
                    dataSnapshotCompletableFuture.completeExceptionally(new BusinessException("Child Remove"));
                }

                @Override
                public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
                    dataSnapshotCompletableFuture.completeExceptionally(new BusinessException("Child MOved"));
                }

                @Override
                public void onCancelled(DatabaseError error) {
                    dataSnapshotCompletableFuture.completeExceptionally(new BusinessException(error.getMessage()));
                }
            });
    return dataSnapshotCompletableFuture;
}

另一种方法是,创建像reservedCustomerIds 这样的新节点,检查customerId 是否已保留,如果未保留,则将该ID 推送到该数组并返回ID 以供使用,在本例中为customerId是一把钥匙:

public CompletableFuture<String> buildUniqueCustomerId() {
    String customerId = "A-".concat(String.valueOf(System.currentTimeMillis()).substring(1, 9).concat(RandomStringUtils.randomNumeric(2)));
    String customerRef = String.format("/reservedCustomerIds/%s", customerId);
    return firebaseDatabaseProvider.fetchObjectAtRef("/usersData".concat(customerRef))
            .thenCompose(dataSnapshot -> {
                if (dataSnapshot.getValue() != null) {
                    return buildUniqueCustomerId();
                } else {
                    return CompletableFuture.completedFuture(customerId);
                }
            })
            .thenCompose((newCustomerId) -> this.updateObjectData(true, customerRef).thenApply(aVoid -> newCustomerId))
            .exceptionally(throwable -> {
                Logger.error(throwable.getMessage());
                return null;
            });
}

第一种方式代码需要一些清理,但它只是快速启动,但是您可以看到第二种方式在代码中更短,但是存储该 ID 需要多一步,而且它会有额外的存储空间reservedCustomerIds 仅用于检查 ID:

"reservedCustomerIds" : {
  "A-4998721187" : true,
  "A-4998722342" : true,
  "A-4998722222" : true,
  "A-4998724444" : true,
  "A-4998725555" : true,
}

哪一个性能最好,检查 customerId 唯一性的速度更快?使用 customerId 作为额外存储的键,或者在配置文件中使用 customerId 本身和.indexOn

P.S:在 cmets 或完整答案中,如果您能给我一个关于如何进行 firebase 索引或查询的链接,我将不胜感激。

【问题讨论】:

  • 两者都是合理的数据模型。你有什么问题?
  • @FrankvanPuffelen 性能更好,假设你有 100 万用户,哪一个更适合查询 customerId。
  • 第二种方法称为扇出,在 Firebase 文档中进行了介绍:firebase.google.com/docs/database/android/structure-data#fanout。它的优点是您无需查询即可读取 ID(这意味着其规模没有实际限制)。但是你需要为每个客户做额外的阅读。这些并不像大多数开发人员预期的那么慢(参见stackoverflow.com/questions/35931526/…),但始终存在可用性限制。
  • 很难说“这比那更好”。如果有的话,Firebase 文档将会明确(并且响亮)关于它。但是更扁平的结构、分散的数据以及不查询数百万个节点来找到 1 都有助于拥有一个平滑扩展的应用程序。另见stackoverflow.com/questions/37884671/…stackoverflow.com/questions/39712833/…
  • @FrankvanPuffelen 如果您有时间将此评论作为答案,以便我接受,我将结束此问题:),谢谢

标签: java firebase firebase-realtime-database


【解决方案1】:

第二种方法称为fanning out and is covered in the Firebase documentation。它的优点是无需查询即可读取 ID,这意味着其规模没有实际限制。

但是您需要为每个客户做额外的阅读。虽然这些并不像大多数开发人员预期的那么慢(参见Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly),但始终存在可用性限制。

很难说“这比那更好”。如果有的话,Firebase 实时数据库文档将会明确(而且响亮)关于它。但是更扁平的结构、分散的数据以及不查询数百万个节点来找到 1 都有助于拥有一个平滑扩展的应用程序。另见Are Firebase queries scalableFirebase Performance: How many children per node?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多