dynamodb的数据建模,其中实体具有一对多和多对多关系

时间:2020-10-13 11:22:29

标签: nosql amazon-dynamodb

我是NoSql世界的新手。我正在用dynamodb构建无服务器应用程序。在关系数据库中,当我有3个实体(例如post,post_likes和post_tags)时,我将只有几个表并使用联接来获取数据。但是,我不知道应该为帖子与喜欢具有一对多关系,而与标签具有多对多关系的情况,如何为NoSql结构做一个设计。

发布模型:

user_id <string>
attachment_url <string>
description <string>
public <boolean>

类似模型:

user_id <string>
post_id <string>
type <string>

标记模型:

name <string>

我的访问模式很少:

  1. 获取所有公开帖子
  2. 通过单个标签和公共状态过滤所有帖子
  3. 通过用户ID获取所有帖子
  4. 通过帖子ID获取单个帖子

并且每次应使用标签数据提取帖子,并且喜欢包含附加到点赞的用户数据的点赞数据。 在关系数据库中,我将创建post_tags表并通过标签获取所有帖子。但是,如何用dynamodb做到这一点?

我正在努力弄清表的外观以及在post_iduser_idtag_namepublic字段中设置为主键和排序键的内容这种情况下?

我最初的想法是用一个看起来像这样的实体建立一个表:

Partition key | Sort key | data attributes 
tag_name      | post_id  | public | user_id | likes[] | other post attributes...

然后该表将如下所示:

enter image description here

我已经设置了2个全局二级索引。 第一全球二级索引:

将分区键设置为public,将排序键设置为post_id

第二个全球二级索引:

将分区键设置为user_id,将排序键设置为post_id

这样,对于每个帖子都有一个标签,我将在表中复制该帖子。我认为通过使用标记作为第一个过滤器,这样我可以有效地查询帖子,如果我需要通过标记进行查询。 / p>

enter image description here

但是,如果我仅通过 public 状态或 user_id 进行查询,我将获得它们所属的每个标签的所有帖子重复信息。

enter image description here

enter image description here

或者我应该在表中有3个单独的实体,分别是标签帖子顶赞,如果我通过标签获取帖子,我将首先执行一个查询以按标签查找所有 post_ids ,然后执行第二个查询以获取帖子及其顶赞 id ,然后执行第三个查询以获取 likes数组。 我不知道什么是最佳做法,因为我只是刚开始使用 dynamodb

这种数据库结构应该是什么样子?

1 个答案:

答案 0 :(得分:1)

通过深入考虑访问方式并定义您的实体(帖子,用户,点赞等),您将有一个良好的开端。如您所知,对访问模式的彻底了解对于将数据存储在DynamoDB中至关重要。

在查看我的答案时,请记住,这只是一个解决方案。 DynamoDB在定义数据模型时为您提供了极大的灵活性,这既是福也是祸!此答案不是为这些访问模式建模的方式。取而代之的是,实现这些访问模式的方法是一种。让我们开始吧!

我想首先列出我们需要建模的实体以及每个实体的主键。在整个本文中,我将使用复合主键,它们是由分区键(PK)排序键(SK)组成的键。让我们从一个空白表开始,然后逐步填写。

         Partition Key             Sort Key
User
Post
Tag

用户

用户是您应用程序的核心,所以我将从这里开始。

让我们先定义一个用户模型,该模型使我们可以通过ID识别用户。我将对用户实体的PK和SK使用模式USER#<user_id>

User entities

这支持以下访问模式(为简单起见,以伪代码为例):

  1. 通过ID获取用户
ddbClient.query(PK = USER#1, SK = USER#1)

我将为用户使用新的PK / SK模式更新表

         Partition Key             Sort Key
User     USER#<user_id>           USER#<user_id>
Post
Tag

帖子

我将通过关注用户与其帖子之间的一对多关系开始对帖子进行建模。

您具有访问模式,可以按UserId提取所有帖子,因此我首先将Post模型添加到User分区。我将通过定义USER#<user_id>的PK和POST#<post_id>的SK来做到这一点。

Users and Posts

这支持以下访问模式:

  1. 获取用户和所有帖子
ddbClient.query(PK = USER#<user_id>)
  1. 获取用户帖子
ddbClient.query(PK = USER#<user_id>, SK begins_with "POST#")

您可能会对奇怪的帖子ID感到好奇。提取帖子时,您可能希望先获取最新的帖子。您还希望能够通过ID唯一标识帖子。有这种要求时,可以使用KSUID作为唯一标识符。解释KSUID有点超出您的问题范围,但要知道它们在创建时是唯一的可排序的。由于DynamoDB通过“排序键”对结果进行排序,因此您对用户帖子的查询将自动按照创建日期进行排序!

为您的应用程序更新PK / SK模式,我们现在有

         Partition Key             Sort Key
User     USER#<user_id>           USER#<user_id>
Post     USER#<user_id>           POST#<post_id>
Tag

标签

关于如何对帖子和标签之间的一对多关系建模,我们有一些选择。您可以在发布项目中添加一个list属性,该属性仅列出该项目中的标签数量。这种方法非常好。但是,看看您的其他访问模式,我现在将采用另一种方法(很明显为什么以后要这么做)。

我将使用POST#<post_id>的PK和TAG#<tag_name>的SK来标记标签。

Post Tags

由于主键是唯一的,因此以这种方式对标签进行建模将确保没有两次用相同的标签对Post进行标签。此外,它使我们在帖子上可以拥有无​​数的标签。

更新Tag的PK / SK表,我们有

         Partition Key             Sort Key
User     USER#<user_id>           USER#<user_id>
Post     USER#<user_id>           POST#<post_id>
Tag      POST#<post_id>           TAG#<tag_name>

至此,我们已经对用户,帖子和标签进行了建模。但是,我们仅解决了四种访问模式之一。让我们看看如何使用二级索引来支持您的访问模式。

注意:您还可以采用完全相同的方式对Likes进行建模。

定义二级索引

二级索引使您可以支持数据中的其他访问模式。让我们定义一个非常简单的二级索引,看看它如何支持您的各种访问模式。

我将创建一个二级索引,该二级索引交换基表中的PK / SK模式。这种模式称为inverted index,看起来像这样:

Inverted Secondary Index

我们在这里所做的所有工作都交换了基表的PK / SK模式,这使我们可以访问另外两种访问模式:

  1. 通过ID获取帖子
ddbClient.query(IndexName = InvertedIndex, PK = POST#<post_id>)
  1. 按标签获取帖子
ddbClient.query(IndexName = InvertedIndex, PK = TAG#<tag_name>)

按公开/私人状态获取所有帖子

您想按公开/私人身份获取帖子,以及获取所有帖子。获取所有 个帖子的一种方法是将它们放在单个分区中。我们可以将公共/私人状态放在排序键中,以将公共和私人帖子分开。

为此,我将在Post项目上创建两个新属性:_typepublicPostId。这些字段将用作我称为PostByStatus的二级索引的PK / SK模式。

完成此操作后,您的基表将如下所示:

new Post attributes

您的新二级索引如下所示:

Posts by public status

该二级索引将启用以下访问模式

  1. 获取所有帖子
ddbClient.query(IndexName = PostByStatus, PK = POST)
  1. 获取所有私人帖子
ddbClient.query(IndexName = PostByStatus, PK = POST, SK begins_with "PRIVATE#")
  1. 获取所有公开帖子
ddbClient.query(IndexName = PostByStatus, PK = POST, SK begins_with "PUBLIC#")

请记住,帖子ID是KSUID的ID,因此,它们自然会在发布帖子的日期之前按您的搜索结果进行排序。

热分区一词

在应用程序扩展时,将所有帖子存储在单个分区中可能会导致hot partition。解决此问题的一种方法是将您的“帖子”项目分布在多个分区中。如何做到这一点完全取决于您,并且取决于您的应用程序。

避免单个POST分区的一种策略可能涉及按创建天/周/月/等对帖子进行分组。例如,您可以使用POST来代替在PostByStatus二级索引中使用POSTS#<month>-<year>作为PK,如下所示:

Avoiding hot partitions

您的应用程序在获取帖子时(例如,从当月开始,然后向后追溯直到获取足够的结果)时,需要考虑这种模式,但是您需要将负载分散到多个分区中。

总结

我希望本练习为您提供一些有关如何建模数据以支持特定访问模式的想法。 DynamoDB中的数据建模需要花费一些时间才能正确完成,并且可能需要多次迭代才能为您的特定应用程序工作。这可能是一条陡峭的学习曲线,但回报是可以为您的应用程序带来规模和速度的解决方案。