为多对多关系场景选择高效的数据模型 Azure Cosmos Db
Picking up an efficient Data model Azure Cosmos Db for many to many relationship scenario
我正在尝试使用 Azure cosmos db 构建应用程序,要求是针对用户存储文章列表。在这种情况下,文章和用户将具有多对多映射。
- 创建将通过我的应用程序公开的批量推送端点发生,它应该期待一篇文章和分配给它的用户列表,它还支持根据文章删除相应的用户。
- 我还应该公开一个端点,它应该提供一个选项来获取针对用户的文章列表。
解决以上两个问题
我计划提供一个名为 userArticles 的容器,其架构以 user/id 作为分区键,并将每篇文章作为单独的文档插入
{
id:guid
type:"article"
article: {
id:1
}
user: {
id: guid
}
}
{
id:guid
type:"article"
article: {
id:2
}
user: {
id: guid
}
}
所以我可以为传递到我的批量推送端点的每个用户 ID 批量插入一个文档。
为了获取文章列表,因为我将分区键设置为 user/id,这是一个点查询,可以解决我的性能问题。
我可以看到我只能通过分区键删除文档,因为 userId 是分区键,我不能使用它,因为我需要单独删除该特定文章,不知道如何我可以在 Cosmos 中完成吗?
注:
一篇文章下可以有百万用户
但是一个用户在他们下面的文章可能只有几个 100
我可以牺牲写入性能但不能牺牲读取性能
有什么方法可以更改模型,以便批量 insert/delete 和读取消耗更少 Ru/s 并获得更好的性能?或者拆分模型会有帮助?
这些问题很难在这里回答,因为 NoSQL 设计需要对应用程序本身有深入的了解。我也不确定我在这里遵循您的用例,但会尝试使用这种简单的方法。很可能适合您的解决方案会更加细致。
首先,要维护 many:many 关系,您需要两个容器。一份给用户,一份给文章。
要针对用户的读取性能进行设计,取决于关系的“多”方有多大,您可以将数组中的文章嵌入到每个用户文档中。但是您需要小心,因为最大文档大小为 2MB。此外,随着文章变得越来越大,更新它们的成本也会越来越高,如果用户文档经常更新,维护起来就会非常昂贵。解决方案是引用数据并将文章作为单独的行添加到用户容器中,并使用 /userId 作为所添加文章的分区键。
您最终得到的是一个带有“/userId”分区键、一个用户文档和多个文章文档的“用户”容器。您将需要消除用户和文章实体之间的歧义,因此您需要“类型”属性,如上所示,值为“用户”或“文章”。要获取用户和所有文档,您只需查询用户“Select * from c where c.userId = 'user a'”。如果您只需要用户,请为“type='user a'”添加过滤器。
另请注意,您将需要同样存储在用户容器中的文章所需的数据,以便在按用户查询时可以获得文章的所有属性。
为了保持数据同步,您需要使用 Change Feed 并监控文章容器,然后每当一篇文章更新时,它就会查询用户容器以获取该文章 ID,然后就地更新每篇文章。请注意,这两个查询都将是跨分区的,并且可能会变得非常昂贵,而且您还将更新同一篇文章的多个实例,因为您需要为每个用户复制它。但是,请记住,您这样做是因为您正在针对用户的读取进行优化。
现在如果您说文章和用户之间存在一对“百万”的关系并且您希望对文章进行快速查询,那么您将不得不在文章容器中执行相同的操作。在这里您肯定需要将用户添加为单独的行。但是您需要留意您的逻辑分区有多大。最大大小为 20 GB。如果你接近这个或者 articles:users 之间的关系是无限的,那么你将需要一个具有更多基数的分区键。我不能告诉你那是什么,但它应该是你总是查询文章容器的东西,或者你也可以尝试加盐分区键值,“article1_1”,“article1_2” "等
同样的事情也适用于维护用户和文章之间的引用完整性。您将需要使用 Change Feed 来监视用户容器,并且每当更新用户时,您都需要查询该用户的文章容器并更新它的每个实例。
我正在尝试使用 Azure cosmos db 构建应用程序,要求是针对用户存储文章列表。在这种情况下,文章和用户将具有多对多映射。
- 创建将通过我的应用程序公开的批量推送端点发生,它应该期待一篇文章和分配给它的用户列表,它还支持根据文章删除相应的用户。
- 我还应该公开一个端点,它应该提供一个选项来获取针对用户的文章列表。
解决以上两个问题 我计划提供一个名为 userArticles 的容器,其架构以 user/id 作为分区键,并将每篇文章作为单独的文档插入
{
id:guid
type:"article"
article: {
id:1
}
user: {
id: guid
}
}
{
id:guid
type:"article"
article: {
id:2
}
user: {
id: guid
}
}
所以我可以为传递到我的批量推送端点的每个用户 ID 批量插入一个文档。
为了获取文章列表,因为我将分区键设置为 user/id,这是一个点查询,可以解决我的性能问题。
我可以看到我只能通过分区键删除文档,因为 userId 是分区键,我不能使用它,因为我需要单独删除该特定文章,不知道如何我可以在 Cosmos 中完成吗?
注: 一篇文章下可以有百万用户 但是一个用户在他们下面的文章可能只有几个 100 我可以牺牲写入性能但不能牺牲读取性能
有什么方法可以更改模型,以便批量 insert/delete 和读取消耗更少 Ru/s 并获得更好的性能?或者拆分模型会有帮助?
这些问题很难在这里回答,因为 NoSQL 设计需要对应用程序本身有深入的了解。我也不确定我在这里遵循您的用例,但会尝试使用这种简单的方法。很可能适合您的解决方案会更加细致。
首先,要维护 many:many 关系,您需要两个容器。一份给用户,一份给文章。
要针对用户的读取性能进行设计,取决于关系的“多”方有多大,您可以将数组中的文章嵌入到每个用户文档中。但是您需要小心,因为最大文档大小为 2MB。此外,随着文章变得越来越大,更新它们的成本也会越来越高,如果用户文档经常更新,维护起来就会非常昂贵。解决方案是引用数据并将文章作为单独的行添加到用户容器中,并使用 /userId 作为所添加文章的分区键。
您最终得到的是一个带有“/userId”分区键、一个用户文档和多个文章文档的“用户”容器。您将需要消除用户和文章实体之间的歧义,因此您需要“类型”属性,如上所示,值为“用户”或“文章”。要获取用户和所有文档,您只需查询用户“Select * from c where c.userId = 'user a'”。如果您只需要用户,请为“type='user a'”添加过滤器。
另请注意,您将需要同样存储在用户容器中的文章所需的数据,以便在按用户查询时可以获得文章的所有属性。
为了保持数据同步,您需要使用 Change Feed 并监控文章容器,然后每当一篇文章更新时,它就会查询用户容器以获取该文章 ID,然后就地更新每篇文章。请注意,这两个查询都将是跨分区的,并且可能会变得非常昂贵,而且您还将更新同一篇文章的多个实例,因为您需要为每个用户复制它。但是,请记住,您这样做是因为您正在针对用户的读取进行优化。
现在如果您说文章和用户之间存在一对“百万”的关系并且您希望对文章进行快速查询,那么您将不得不在文章容器中执行相同的操作。在这里您肯定需要将用户添加为单独的行。但是您需要留意您的逻辑分区有多大。最大大小为 20 GB。如果你接近这个或者 articles:users 之间的关系是无限的,那么你将需要一个具有更多基数的分区键。我不能告诉你那是什么,但它应该是你总是查询文章容器的东西,或者你也可以尝试加盐分区键值,“article1_1”,“article1_2” "等
同样的事情也适用于维护用户和文章之间的引用完整性。您将需要使用 Change Feed 来监视用户容器,并且每当更新用户时,您都需要查询该用户的文章容器并更新它的每个实例。