DynamoDB Table 设计:如何为一对多关系建模,我需要所有 "one" 项和 "many" 项中的一个按某些属性排序

DynamoDB Table Design: How do I model a one to many relationship where I need all of the "one" items and one of "many" sorted by some attribute

我的整个职业生涯都在处理非规范化关系数据库。为了实现一个单一的 table 设计,可以处理“App Store”之类的个人项目中的几个特定访问模式,我很难摆脱所有这些。

Here's a quick ERD. 有一个由平台(iOS、Android)和包标识符标识的 App 模型以及创建新版本时使用的默认映射。每个应用程序可以有 0 到多个版本,这些版本由版本号标识(这是一个连续的数值,在应用程序的上下文中是唯一的)。一个版本具有 IsReleased 属性以及其他几个属性(如名称、发行说明、二进制路径等)。

访问模式

  1. 列出每个应用程序的最新版本。
  2. 列出给定平台的每个应用程序的最新版本。
  3. 列出 IsReleased 为 1 的每个应用程序的最新版本。
  4. 列出 IsReleased 为 1 的给定平台的每个应用程序的最新版本。
  5. 获取特定应用的最新版本。
  6. 获取 IsReleased 为 1 的特定应用的最新版本。
  7. 获取特定应用的所有版本。
  8. 获取 IsReleased 为 1 的特定应用的所有版本。
  9. 获取特定应用程序的默认属性。

我在处理 1 到 4 时遇到了问题,这个 table 正是我要去的地方。我很难找到一个 GSI,它将给我所有的 app 项目,按排序顺序只有一个 version

pk sk Defaults App Name Version IsReleased Other Attributes
app_ios_com.app.one defaults { ... json ... }
app_ios_com.app.one version_1 App One 1 1
app_ios_com.app.one version_2 App One 2 1
app_ios_com.app.one version_3 App One 3 1
app_ios_com.app.two defaults { ... json ... }
app_ios_com.app.two version_1 App Two 1 1
app_ios_com.app.two version_2 App Two 2 0
app_ios_com.app.two version_3 App Two 3 0

例如,对于访问模式 1,我想要:

pk sk Defaults App Name Version IsReleased Other Attributes
app_ios_com.app.one version_3 App One 3 1
app_ios_com.app.two version_3 App Two 3 0

例如,对于访问模式 3,我想要:

pk sk Defaults App Name Version IsReleased Other Attributes
app_ios_com.app.one version_2 App One 3 1
app_ios_com.app.two version_1 App Two 1 1

我必须记住的一些数据限制:

我觉得解决方案就在我面前,但我不能指手画脚。

TLDR; 想到的解决方案是 leaderboard pattern to cache the latest app versions in separate record(s). Whenever a new version is added, DynamoDB Streams 将更改作为事件发送到 lambda,然后更新非规范化的最新记录。

注意:您出色的文章中缺少一条信息:您需要多久执行一次 latest 查询?如果不是很频繁,那么“扫描完成”对于您当前的卷就可以了。如果答案是每分钟 1k latest 次查询,那就另当别论了。好消息是您的基本 table 设计很可靠。 Latest 可以在出现 performance/cost 问题时逐步实施查询优化,而不会扰乱 table 设计。

反规范化最新版本

我们将保留最新版本的非规范化副本,这是另一个听起来有罪的 DynamoDB pattern。当添加版本或更改发布状态时,流触发的 lambda 将使用更新 API 更新这些记录。如何存储最新版本信息?我们有几种选择:

  1. 将所有 latest 数据存储在具有地图属性 {app1: {latest version copy}, app2: ...} 的单例记录中。您可以将更多逻辑放入记录中以处理 isReleased 项,或者只需在后端获取记录和过滤器。
  2. 使用全球二级索引,每个应用有一条记录。每条记录都有“最新”作为 app_id 的 GSI1PK 和 GSI1SK。记录 与#1 中的信息相同。
  3. 用作 GSI,每个应用有多个记录。这样的事情似乎有效。例如,查询 #4 将使用 GSI1PK=Latest#Released AND begins_with(GSI1SI, "IOS")
GSI1PK              GSI1SK
Latest              app_ios_com.app.one
Latest              IOS#app_ios_com.app.one
Latest#Released     app_ios_com.app.one
Latest#Released     IOS#app_ios_com.app.one

注意:如果您的查询量大而基数小,hot partitions may be a problem for these "leaderboard" type deormalised patterns. If this becomes a problem, you can address it by keeping multiple copies of each "latest" record, e.g. have X copies that are queried randomly latest-copy1, latest-copy2, latest-copy3. Amazon calls this pattern sharding using calculated suffixes.