【问题标题】:Firebase multilocation update in a transaction?交易中的 Firebase 多位置更新?
【发布时间】:2016-10-06 00:47:18
【问题描述】:

我需要帮助找出在我的用例中使用 firebase 的最佳方式。我正在制作 Twitter 克隆,但与 FireFeed 不同的是,我的帖子是可变的。

我的数据库结构如下:

房间,本质上是您可以订阅的帖子的提要。房间的成员可以向该房间写帖子:

  1. rooms/{roomId}/posts/{postId}/ -> 包含标题、cmets 数量、喜欢等属性的帖子摘要
  2. rooms/{roomId}/users/{userId}/ -> 订阅此房间的所有用户列表
  3. rooms/{roomId}/info/ -> 房间大小、类型、名称等

用户,所有用户的全局列表:

  1. users/{userId}/feed/{postId} -> 来自用户订阅的所有房间的所有房间/{roomId}/posts/ 的聚合
  2. users/{userId}/rooms/{roomId} -> 用户订阅的所有房间列表

帖子,实际帖子内容的全局列表:

  1. posts/{postId}/cmets/{commentId} -> 嵌套 cmets
  2. posts/{postId}/users/{userId} -> 想要在此帖子上推送通知的用户
  3. posts/{postId}/info/ -> cmets 数量、点赞数、作者、发布日期等
  4. posts/{postId}/content/ -> 实际内容

学校、房间列表:

  1. schools/{schoolId}/rooms/{roomName}/info -> 学校房间列表。房间名称在学校内是唯一的
  2. schools/{schoolId}/admins/{userId} -> 具有不同授权规则的管理员列表

现在,前两个用例很好。困惑来自于如何制作和编辑对帖子的引用。我的用例是:

用例 0:用户想要创建一个房间。

  1. 多位置将房间对象更新到 /rooms/,并将适当的房间摘要对象更新到学校/{schoolId}/rooms/ 和 users/{userId}/rooms/。

用例 1:用户想要订阅一个房间。

  1. 在房间/{roomId}/ 上运行事务。将 {userId} 添加到 rooms/{roomId}/users/ 并将房间大小增加 1。
  2. 事务完成后,拍摄快照并进行多位置更新。将房间/{roomId}/posts 复制到 users/{userId}/feed,将 {roomId} 添加到 user/{userId}/rooms/ 并将新大小写入 /schools/{schoolId}/rooms/{roomName}/info .
  3. 如果多写失败,则从房间/{roomId}/users/ 中删除 {userId}。

用例 2:用户想要在房间里发帖。

  1. 从房间/{roomId}/users 中检索用户列表
  2. 使用帖子摘要和帖子对象在多位置更新所有用户提要和全局帖子表。

这样的问题是,如果另一个用户在我们的用户检索用户列表后立即加入这个房间,那么他就不是接收这个新帖子的多地点更新的一部分。

当我尝试更新帖子中的 cmets 数量时,我面临着类似的难题。如果一个帖子有 15 个 cmets,并且有 2 个人同时对该帖子添加新评论,则全局帖子对象在事务完成时将正确更新为 17,但根据之后扇出的顺序,每个人的帖子摘要对象可能有 16 cmets。当用户加入房间时,房间的大小也是如此。

我应该怎么做?有没有办法更好地为我的数据建模,或者有没有办法在事务期间正确地进行多位置更新?

【问题讨论】:

  • 没有 API 可以进行多位置更新,该更新采用它更新的(某些)节点的当前值。常见的选项是:1)在树中运行足够高的事务(这限制了并发性),2)更改数据结构以将共享数据向下推送到树中,3)使用受信任的进程(例如小节点。 js 服务器)来运行事务中的逻辑。
  • 还可以在这里查看我的(稍微相关的)答案:stackoverflow.com/questions/30693785/…
  • @FrankvanPuffelen 你能详细说明一下2吗?如何更改数据结构?

标签: firebase firebase-realtime-database


【解决方案1】:

当我看到您的用例时,我认为您的数据模型有点过于复杂。

Firebase 对我来说就像:

  1. 无sql数据存储
  2. 发布/订阅服务器
  3. 启用 web-sockets 的前端服务器
  4. 客户端库

这意味着我不会存储这个users/{userId}/feed/{postId},而是在客户端登录时执行以下操作:

  1. “连接”到用户订阅的每个房间
  2. 从订阅的房间中获取所有最近的帖子
  3. 在客户端排序帖子

这样,当有人发表新帖子/评论时,它只会写在一个位置rooms/{roomId}/posts/

我还将删除您的点赞/发布/评论计数器,当我想要此信息时,请调用方法 numChildren 以避免不必要的并发访问。

【讨论】:

  • 嗯,我之前按照您所说的那样对数据进行了结构化,但是客户端重新排序和分页并非易事。获取最近 100 个帖子的页面意味着从所有房间获取 100 个帖子,以确保它们按最近排序。这很快就会失控。除此之外,我想优化读写。 Tbh,我可以接受一个错误以及不同统计数据(如喜欢、用户等)的计数不一致,但我不能接受用户错过房间帖子的可能性。跨度>
  • 我不得不承认第一次加载可能很难实现,但为什么不从每个房间获取最新的帖子并将它们展示给客户。一旦你的页面/应用加载了一些帖子,检查是否有有趣的旧帖子,就像在某些聊天应用中完成的那样,只加载最新消息而不是所有未读消息
  • 如果所有房间的活跃度大致相同,那么从每个房间获取 100/n 个最近的帖子以显示 ~100 个帖子会起作用,但我的域不允许我对不同房间的活动做出任何这样的假设房间。即使房间同样活跃,每次读取都需要每个用户订阅的房间一个网络 RTT,而写入只需要一个。我宁愿把它反过来并针对读取进行优化。
  • 我从来没有说过这是一个完美的解决方案,只是给出了一个想法,但你说“每次读取都需要每个房间一个网络 RTT,而用户订阅了一个网络 RTT,而写入只需要一个”不! . Firebase 使用 Web 套接字。客户端上的任何更新都会传输到服务器,而无需启动新连接,因为连接已经存在。当连接已经存在时,服务器将更新推送到其他连接的(和感兴趣的)客户端。即:您的加载时间仅取决于您的数据长度,“RTT”在 Firebase 中可以忽略不计
  • 啊,我不知道。谢谢!
猜你喜欢
  • 2021-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多