【问题标题】:Data modelling for 'followers count' in MongoDB app with RESTful API使用 RESTful API 在 MongoDB 应用程序中对“关注者数量”进行数据建模
【发布时间】:2013-05-09 05:51:35
【问题描述】:

我有一个 User 实体,其中包含他/她正在关注的其他用户 ID 数组。

var userSchema = {
  following: { type: Array }
};

我有一个 RESTful API,当我请求 User 时,我的应用程序需要知道 User 有多少关注者。

我看到的两个选项是:

  • 当请求User 时,进行计数查询,例如:{ following: { $in: [userId] } }
  • UserPOSTed 时,检查是否有任何用户ID 添加或删除到following 数组,如果是,使用MongoDB 的$inc 增加或减少followersCount User 文档上已关注/取消关注的属性。

他们还有其他选择吗?如果没有,最好的选择是什么?将followersCount 属性放入文档本身我觉得有点奇怪,因为这表明它可以由应用程序更新,而实际上它只是一个动态计数。

我有类似的情况,我需要我的 RESTful API 来返回与 website 关联的 articles 的数量。我是根据请求计算该网站的文章数,还是存储计数属性并在每次创建与该网站相关联的新文章时更新它?

【问题讨论】:

  • 您可能想要考虑(并在问题中包含)您的读/写负载是什么 - 在决定 MongoDB 中的架构时,您如何使用数据以及围绕响应性/延迟的要求至关重要(以及在所有非关系系统中)。
  • " 我觉得在文档本身中添加 followerCount 属性有点奇怪,因为这表明它可以由应用程序更新,而实际上它只是一个动态计数。"我有同样的感觉,并通过在我放置所有计算字段的文档中计算一个对象c 来“解决”它,在这种情况下为followersCount。它提供了一些(心理上)关注点的分离,尽管它当然不是无懈可击的。使用后台作业重新计算仅在 c 上执行。

标签: api mongodb rest


【解决方案1】:

不要害怕在文档中存储比您认为需要的更多的数据。当我开始按照 mongo 而不是 RDMS 的思路思考时,我摆脱了规范化一切的想法。它允许我在文档中添加更多“有用”的信息,即使它不是绝对必要的。文档中的事务很便宜,存储成本也相对便宜。在另一个文档或集合中查找信息的成本很高(处理方面),如果可以的话,可能值得降低。

我理解您对数据库中“应用程序可以编辑”的计数的担忧。所以呢?确保您的应用程序无法对其进行编辑,并且如果您的 API 向全世界开放,请确保他们也无法对其进行编辑。 “缓存”计数的用处远远超过了它可能被编辑和不同步的事实。这是使用 nosql 数据库的危险之一:事情最终可能不同步,因为我们信任应用程序代码来保持数据井井有条,而不是依赖数据库。

考虑一下。如果用户被删除,并且计数没有更新怎么办。这会给用户带来不同的世界吗?可能不是。虽然我喜欢后台任务来检查值的想法(并且值得拥有类似的东西),但另一种解决方案是每次用户向关注者发布内容时检查完整性。在这种情况下,无论如何您都必须获得所有用户,因此检查计数也相对便宜。本质上,您正在构建一个故障保护,每次从用户发送消息时都会检查数据完整性。如果用户从不发送,则该用户可能不在乎计数是否 100% 准确。

归根结底,如果您的代码经过了良好的测试,并且遵循了防御性编码实践,那么您就不应该有任何一致性问题。

【讨论】:

    【解决方案2】:

    您想要的方式就是正确的方式。 MongoDB 之类的优点之一是您可以嵌入数据以减少您需要在页面上运行的查询。这就是你通过缓存关注者数量所做的事情。

    您的两种选择都有效,只是您想要做出的权衡。

    1) 效率较低,但最准确。当他们下一次关注某人时,任何以某种方式发生的不同步都会被修复。这是一个不错的开始方式,然后在关注者人数较多或用户数据库变大时进行更改。

    2) 最有效的是,您需要确保在更改关注者数量时始终调用 $inc,否则它将变得不同步,直到您执行 #1。

    或者,您可以执行第 2 步,然后在后台运行协调作业,以便在它确实变得不同步时重新同步它。您可以使用 MongoDB aggregation framework 轻松完成此操作,您可以通过使用 User 集合中的时间戳来加快协调工作,并且只为自上次运行作业后更新的人重新计算关注者。

    随着原始问题的更新而扩展:

    一般来说,如果您可以避免使用count(),您应该这样做。

    从使用count() 开始,然后换成更有效的解决方案(#2)并没有错。在 memcache 之类的地方缓存 count() 结果也可用于减少性能问题,直到您停止直接使用 count()

    【讨论】:

    • 选项一会如何发生不同步?如果要扩展到成千上万的用户,您认为 MongoDB 是否合适?
    • 如果您正在运行复制,由于复制滞后,对从属服务器的查询可能不准确。如果你永远不会运行复制,那么它不应该真的可以异步。至于扩展,这取决于您的服务器是什么,但是成千上万的用户就可以了。唯一的问题是 Mongo 的计数速度可能很慢,所以值得关注。这绝对不是您需要过多担心的问题。
    • 如果同一个线程在数组后面更新但在增加/删除用户的计数器之前死亡,则可能不同步(请注意,数组反映了该用户关注的对象,并且计数被保留关于有多少用户正在关注特定用户,因此它不是同一文档中的冗余信息)。您还可以让“维护”作业使用聚合框架而不是 map/reduce - 它会快得多。
    • 这种情况将相当罕见。每隔一段时间进行一次回忆阶段并不是一个坏主意,以防万一你是那种偏执狂。但是,如果您不执行 SIGKILL 之类的操作,则不会真的发生请求中途终止的情况。
    • 当然它可能会发生——进程本身不必死,也许 mongod 会被关闭——没有事务机制来确保两个单独的写入总是“一起”执行。
    猜你喜欢
    • 1970-01-01
    • 2020-10-30
    • 2011-10-26
    • 2017-08-16
    • 2014-07-15
    • 1970-01-01
    • 2021-02-13
    • 2020-11-12
    • 2013-05-09
    相关资源
    最近更新 更多