【发布时间】: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,使用queryByChild和customerId被索引:
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