在 DynamoDB 中建模 Parent/Child/Subchild 关系

Modelling Parent/Child/Subchild relationships in DynamoDB

我正在开发以下休息服务:

GET /parents - 获取所有 parents
GET /parents/{id} - 通过 id
获取 parent POST /parents - 添加新的 parent

GET /parents/{pid}/children - 获取所有 children for parent pid
GET /parents/{pid}/children/{id} - 通过 id 获取 child for parent pid
POST /parents/{pid}/children - 为 parent pid

添加新的 child

GET /parents/{pid}/children/{cid}/subchildren - 获取 parent pid 和 child cid
的所有 subchildren GET /parents/{pid}/children/{cid}/subchildren{id} - 通过 id 获取 subchildchild for parent pid 和 child cid
POST /parents/{pid}/children/{cid}/subchildren - 为 parent pid 和 child cid

添加新的 subchild

我必须使用 DynamoDB(或 AWS 上的其他一些无服务器且廉价的数据库选项)来执行 CRUD 操作(到目前为止,更新和删除不是优先事项)。 当调用 GET /parents 时,响应将是:

[
  {
    "id": 1,
    "name": "parent1",
    "children": [
      {
        "id": 1,
        "name": "parent1_child1",
        "subchildren": [
          {
            "id": 1,
            "name": "parent1_child1_subchild1"
          },
          {
            "id": 2,
            "name": "parent1_child1_subchild2"
          }
        ]
      },
      {
        "id": 2,
        "name": "parent1_child2",
        "subchildren": [
          {
            "id": 1,
            "name": "parent1_child2_subchild1"
          },
          {
            "id": 2,
            "name": "parent1_child2_subchild2"
          },
          {
            "id": 3,
            "name": "parent1_child2_subchild3"
          }
        ]
      }
    ]
  },
  {
    "id": 2,
    "name": "parent2",
    "children": [
      {
        "id": 1,
        "name": "parent2_child1",
        "subchildren": [
          {
            "id": 1,
            "name": "parent2_child1_subchild1"
          }
        ]
      },
      {
        "id": 2,
        "name": "parent2_child2",
        "subchildren": [
          {
            "id": 1,
            "name": "parent2_child2_subchild1"
          },
          {
            "id": 2,
            "name": "parent2_child2_subchild2"
          },
          {
            "id": 3,
            "name": "parent2_child2_subchild3"
          },
          {
            "id": 4,
            "name": "parent2_child2_subchild4"
          }
        ]
      }
    ]
  }
]

我应该如何为 DynamoDB 中的数据建模?我正在使用 spring-data-dynamodb 来执行存储和检索选项。 在这种情况下,创建模型的最佳设计是什么 类?
目前我已经创建了 https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SampleData.CreateTables.html#SampleData.CreateTables2

中提到的不同表格

你可以做几件事:

A:更适合 cost/performance 但需要一些额外的工作

在你的 table 中,创建一个分区键 PK 和顺序键 OK 作为两个字符串(当然你可以使用任何你想要的键名,这只是一个例子——我的意思是,键不在你的数据模型中,你必须在插入时创建它们)。创建项目时,不要将它们存储为一个大文件,而是将 parents、children 和 subchildren 分开存储。每个项目的 PKparent/child/subchildOK 您在每个项目的 name 中的值它具有正确的 parentname_childname_subchildname 继承类型,或者您可以从每个项目的 parents 属性构造它。

检索后,您可以获得给定 parent 的所有子 children,例如使用 where PK = subchild and OK begins_with parentname_ 等表达式有效地 returns 所有项目subchilds 并且属于给定的 parent,无论 child 是什么。如果您还想在 child 上进行过滤,您可以执行 begins with parentname_childname_。如果你只想要一个parent的children,你可以做where PK = child and OK begins_with parentname_。如果你确切地知道你想要什么child,而不是使用begins_with,你可以简单地做where PK = child and OK = parentname_childname。等等......(我使用 SQL-like 语法来简化 writing/reading 但它很容易转换为 dynamodb 查询语言)。 这个想法过于区分你的项目的实际属性和你如何检索你的项目。如果可以帮助您轻松找到您的物品,您可以创建完全伪造的钥匙!

您可能根本不需要区分 PK 中的 child“级别”,您也可以只使用通用值来获得类似的结果。此方法的主要 objective 是在特定分区键中使用 begins_with 的顺序键。

此方法的主要问题是,一旦您拥有 /parents 示例中的所有 parents/children/subchildren,您将不得不完全用代码重建树。但就dynamodb而言,这可能是最优化的解决方案。

B:使用过滤器更简单,但(更)糟糕 cost/performance

将每个 parent 存储在一个大文档中,然后使用像 where item.children[].subchildren[].name = parentname_childname_subchildname 这样的过滤器扫描您的 table。它效率不高并且成本更高,因为过滤器应用于查询结果,因此您首先检索所有内容然后仅过滤您想要的内容,因此您首先支付所有项目然后删除那些您不想要的.它有效,但不是很好,特别是如果你想要 100000 件中的 5 件。

主要优点是,对于 /parents 调用,您可以获得所描述的整个层次结构。但对于子路径 /parents/x/children/...,您需要重新格式化代码中的 object 或仅查询投影表达式中的子属性。

C: 使用本地二级索引

有点像解决方案 A,您在其中单独存储项目,所有项目具有相同的 PK,但具有具有 3 个不同顺序键的本地二级索引:name_parentname_childname_subchild.然后您可以根据需要分别查询每个索引。对于此用例,使用 LSI 比 GSI 更适合成本优化,但您可以自由地使用 GSI 实现相同的想法。它也可以正常工作(关于重新格式化 parent 元素以包含 children 元素的相同限制)。