【问题标题】:How to structure relationships in Azure Cosmos DB?如何在 Azure Cosmos DB 中构建关系?
【发布时间】:2018-12-19 14:09:27
【问题描述】:

我在 cosmos 的同一个集合中有两组数据,一组是“帖子”,另一组是“用户”,它们由用户创建的帖子链接。

目前我的结构如下;

// user document
{
id: 123,
postIds: ['id1','id2']
}

// post document
{
id: 'id1',
ownerId: 123
}
{
id: 'id2',
ownerId: 123
}

我对这个设置的主要问题是它的可替代性,代码必须强制链接,如果有错误数据很容易丢失,并且没有明确的恢复方法。

我还关心性能,如果用户有 10,000 条帖子,那么我将不得不做 10,000 次查找来解决所有帖子..

这是对实体关系建模的正确方法吗?

【问题讨论】:

  • 您正在尝试以一种不适合使用的方式使用 Cosmos DB。 CosmosDB 不是关系数据库,在对象建模方面没有任何限制。您应该以永远不必执行跨分区查询的方式设计您的 Cosmos 集合。目前的设计看起来需要这样。
  • 那么所有的帖子对象都会直接存在于用户身上吗?但是,如果我想显示每个人发布的所有帖子,我就必须查询每个用户并从中获取帖子?
  • 但是,如果帖子可以被任何用户“点赞”,那又是如何建模的呢?投票用户的 id 将存储在所有者用户内部的帖子中吗?然后为了获得用户的所有喜欢,必须查询用户......这应该是如何工作的?
  • 这取决于您的分区策略以及对谁可以修改您的数据库的严格要求。我会写一个详细的答案。
  • @NickChapsas - 这里真的没有“正确”的答案。有很多方法可以对这些数据进行建模,每种方法都有优点和缺点。有一些选项可以将规范化(关系)ids(带数组?)与非规范化数据混合以快速显示(例如,嵌入用户文档中的最新帖子的 sn-p,带有/id+简短描述的子文档数组?)。存储“喜欢”也是如此。而且我们没有关于如何使用/显示这些数据、读取和写入繁重的工作负载等方面的信息。这是一个非常棒的讨论,但是......它并不适合这里,因为它的性质非常广泛。

标签: azure azure-cosmosdb


【解决方案1】:

正如大卫所说,这是一个很长的讨论,但它是一个非常普遍的讨论,因为我有一个小时左右的“空闲”时间,我很高兴尝试回答它,一劳永逸,希望。

为什么要标准化?

我在您的帖子中注意到的第一件事:您正在寻找某种程度的参照完整性 (https://en.wikipedia.org/wiki/Referential_integrity),当您将更大的对象分解为其组成部分时,这是需要的。也称为标准化。

虽然这通常在关系数据库中完成,但它现在也在非关系数据库中变得流行,因为它有助于避免数据重复,这通常会产生比它解决的问题更多的问题。

https://docs.mongodb.com/manual/core/data-model-design/#normalized-data-models

但是你真的需要它吗?由于您选择使用 JSON 文档数据库,您应该利用它能够存储整个文档的事实,然后将文档与所有所有者数据一起存储:姓名、姓氏或您拥有的有关用户的所有其他数据谁创建了文档。是的,我是说您可能想要评估没有帖子和用户,而只是帖子,其中包含用户信息。这实际上可能非常正确,因为您一定会获得现有用户的确切数据在帖子创建的那一刻。例如说我创建了一个帖子,我有传记“X”。然后我将我的传记更新为“Y”并创建一个新帖子。这两个帖子会有不同的作者传记,这恰到好处,因为它们准确地捕捉到了现实。

当然,您可能还想在作者页面中显示传记。在这种情况下,您将遇到问题。你会用哪一个?可能是最后一个。

如果所有作者为了存在于您的系统中,都必须发布博客文章,这可能就足够了。但也许您希望作者撰写其传记并在您的系统中列出,甚至在他撰写博文之前。

在这种情况下,您需要对模型进行标准化并创建一个新的文档类型,仅供作者使用。如果这是您的情况,那么您还需要弄清楚如何处理前面描述的情况。当作者要更新自己的传记时,你会只是更新作者文档,还是创建一个新的?如果您创建一个新帖子,以便跟踪所有更改,您是否还会更新所有以前的帖子,以便他们引用新文档?

正如您所见,答案很复杂,并且真正取决于您想从现实世界中获取什么样的信息。

所以,首先,弄清楚您是否真的需要将帖子和用户分开。

一致性

假设您确实希望将帖子和用户保存在单独的文档中,从而规范化您的模型。在这种情况下,请记住 Cosmos DB(但通常是 NoSQL)数据库不提供任何类型的本机支持来强制引用完整性,因此您几乎只能靠自己。当然,索引可以提供帮助,因此您可能希望索引 ownerId 属性,以便在删除作者之前,例如,您可以有效地检查他/她是否有任何博客文章,否则将保持孤立状态。 另一种选择是手动创建另一个文档并保持更新,对于每个作者,该文档会跟踪他/她所写的博客文章。使用这种方法,您只需查看此文档即可了解哪些博客文章属于某个作者。您可以尝试使用触发器自动更新此文档,或者在您的应用程序中执行此操作。 请记住,当您在 NoSQL 数据库中进行规范化时,保持数据一致是您的责任。这与关系数据库完全相反,在关系数据库中,您的责任是在对数据进行反规范化时保持数据的一致性。

表现

性能可能是个问题,但您通常不会首先建模以支持性能。您建模是为了确保您的模型可以表示和存储您需要的来自现实世界的信息,然后您对其进行优化,以便在您选择使用的数据库中获得良好的性能。由于不同的数据库将具有不同的约束,因此模型将被调整以处理这些约束。这无非就是古老的“逻辑”与“物理”建模讨论。

在 Cosmos DB 的情况下,您不应该有跨分区的查询,因为它们更昂贵。

不幸的是,分区是您一劳永逸的选择,因此您确实需要清楚地知道您最想支持的最常见用例是什么。如果您的大部分查询是按作者完成的,我会按作者进行分区。

现在,虽然这似乎是一个聪明的选择,但只有当你有很多作者时才会这样做。例如,如果您只有一个分区,那么所有数据和查询都将进入一个分区,从而限制了您的性能。请记住,事实上,Cosmos DB RU 是在所有可用分区中分割的:例如,如果 10.000 RU,您通常会得到 5 个分区,这意味着您的所有值都将分布在 5 个分区中。每个分区的上限为 2000 RU。如果您的所有查询只使用一个分区,那么您真正的最大性能是 2000 而不是 10000 RU。

我真的希望这可以帮助您开始找出答案。我真的希望这有助于促进和发展我认为现在真正成熟的讨论(如何为文档数据库建模)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-15
    • 1970-01-01
    • 2018-03-02
    • 2021-12-01
    • 2020-12-22
    • 2018-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多