DynamoDB 邻接列表是否应该使用离散的分区键来为每种类型的关系建模?

Should DynamoDB adjacency lists use discrete partition keys to model each type of relationship?

上下文

我正在构建一个论坛并研究使用 DynamoDB 和邻接列表对数据进行建模。一些顶级实体(如用户)可能与其他顶级实体(如评论)有多种类型的关系。

要求

例如,假设我们希望能够执行以下操作:

所以,我们基本上是多对多(用户 <=> 评论)对多(喜欢或关注)。

注意:这个例子是故意精简的,在实践中会有更多的模型关系,所以我想在这里想一些可扩展的东西。

基线

以下顶级数据可能在任何邻接表表示中都很常见:

First_id(Partition key)         Second_id(Sort Key)         Data
-------------                   ----------                  ------
User-Harry                      User-Harry                  User data
User-Ron                        User-Ron                    User data
User-Hermione                   User-Hermione               User data
Comment-A                       Comment-A                   Comment data
Comment-B                       Comment-B                   Comment data
Comment-C                       Comment-C                   Comment data

此外,对于下面的每个 table,将有一个等效的全局二级索引,其中交换了分区键和排序键。

示例数据

这是我想在 DynamoDB 中建模的内容:

  1. Harry 喜欢评论 A
  2. Harry 喜欢评论 B
  3. Harry 关注评论 A
  4. 罗恩喜欢评论 B
  5. 赫敏喜欢评论 C

选项 1

使用第三个属性定义关系类型:

First_id(Partition key)         Second_id(Sort Key)         Data
-------------                   ----------                  ------
Comment-A                       User-Harry                  "LIKES"
Comment-B                       User-Harry                  "LIKES"
Comment-A                       User-Harry                  "FOLLOWS"
Comment-B                       User-Ron                    "LIKES"
Comment-C                       User-Hermione               "FOLLOWS"

这种方法的缺点是查询结果中存在冗余信息,因为它们会 return 额外的您可能不关心的项目。例如,如果你想查询喜欢给定评论的所有用户,你还必须处理关注的所有用户] 给出的评论。同样,如果要查询一个用户喜欢的所有评论,则需要处理一个用户关注.[=17]的所有评论=]

选项 2

修改键来表示关系:

First_id(Partition key)         Second_id(Sort Key)
-------------                   ----------
LikeComment-A                   LikeUser-Harry
LikeComment-B                   LikeUser-Harry
FollowComment-A                 FollowUser-Harry
LikeComment-B                   LikeUser-Ron
FollowComment-C                 FollowUser-Hermione

这使得独立查询变得高效:

  1. 评论赞
  2. 评论如下
  3. 用户喜欢
  4. 用户关注

缺点是同一个顶级实体现在有多个键,随着更多关系的添加,这可能会使事情变得复杂。

选项 3

完全跳过邻接表并使用单独的 tables,可能一个用于 Users,一个用于 Likes,一个用于 Follows

选项 4

传统关系数据库。虽然我不打算走这条路,因为这是一个个人项目,我想探索 DynamoDB,但如果 思考事情的正确方式,我很想听听为什么。

结论

感谢您阅读到这里!如果我能做些什么来简化问题或澄清任何事情,请告诉我:)

我看过AWS best practices and this many-to-many SO post,但似乎都没有解决多对多(有很多)关系,因此非常感谢任何资源或指导。

您的选项 1 不可行,因为它没有唯一的主键。在您的示例数据中,您可以看到 (Comment-A, User-Harry).

有两个条目

解决方案 1

实现您正在寻找的方法是为您的 table 和 GSI 使用稍微不同的属性。如果哈利喜欢评论A,那么你的属性应该是:

hash_key: User-Harry
gsi_hash_key: Comment-A
sort_key_for_both: Likes-User-Harry-Comment-A

现在您在 table 和 GSI 中的顶级实体只有一个分区键值,您可以使用 begins_with 运算符查询特定的关系类型。

解决方案 2

您可以将关系设为顶级实体。例如,Likes-User-Harry-Comment-A 将在数据库中有两个条目,因为它“毗邻”User-HarryComment A

如果您想在未来对关系的更复杂信息建模(包括描述关系之间关系的能力,例如 Likes-User-Ron-User-Harry Causes Follows-User-Ron-User-Harry).

但是,这种策略需要在数据库中存储更多的项目,这意味着保存一个“赞”(以便可以查询)不是一个原子操作。 (但是您可以通过只编写关系实体来解决这个问题,然后使用 DynamoDBStreams + Lambda 为我在此解决方案开头提到的两个条目编写条目。)

更新:使用 DynamoDB 事务,以这种方式保存 "like" 实际上可以是完全 ACID 操作。