【问题标题】:DynamoDB partition key choice for notes app笔记应用程序的 DynamoDB 分区键选择
【发布时间】:2018-04-27 14:31:00
【问题描述】:

我想创建一个 DynamoDB 表,以便我保存来自用户的注释。

我拥有的属性:

  • user_id
  • note_id (uuid)
  • 类型
  • 文字

我需要的主要查询:

  • 获取某个用户的所有笔记
  • 获取特定注释
  • 获取特定类型的所有笔记(使用较少的查询)

我知道就性能和 DynamoDB 分区而言,note_id 将是正确的选择,因为它们是独一无二的,并且会平均分布在分区上,但另一方面,如果不扫描所有项目,则更难获取用户的所有注释或使用 GSI。如果它们是唯一的,我想拥有一个排序键没有任何意义。

另一种选择是使用 user_id 作为分区键,使用 note_id 作为排序键,但如果我的某些用户的笔记数量比其他用户多得多,这不会影响我的性能吗?

最好有一个唯一的分区键(如 note_id)以与 DynamoDB 分区一起很好地扩展并使用 GSI 创建我的查询,还是使用分区键代替我的主查询 (user_id)?

谢谢

【问题讨论】:

  • 您的两次二次搜索是否真的独立于知道 user_id 而发生?我在自己做一些复杂的关键设计时发现了你的问题,你似乎过度概括了这些场景。

标签: amazon-web-services nosql amazon-dynamodb


【解决方案1】:

可能最简单且最具成本效益的方法是单个表:

表结构

  • note_id (uuid) / 哈希键
  • user_id
  • 类型
  • 文字

有两个 GSI,一个用于“获取某个用户的所有笔记”,一个用于“获取某个类型的所有笔记(使用较少的查询)”:

GSI 表示“获取某个用户的所有笔记”

  • user_id / 哈希键
  • note_id (uuid) / 范围键
  • 类型
  • 文字

关于此的一点说明 - 您最常见的查询是“获取某个用户的所有注释”还是“获取特定注释”?如果是前者,那么您可以将 GSI 密钥交换为表格密钥,反之亦然(如果这有意义 - 本质上,将您的 user_id + note_id 作为表格的密钥,将 note_id 作为GSI 密钥)。这也取决于你如何构建你的user_id - 我怀疑你已经学会了;确保您的 user_id 不是连续的 - 将其设为 UUID 或类似名称。

GSI 表示“获取某种类型的所有笔记(较少使用的查询)”

  • 类型/哈希键
  • note_id (uuid) / 范围键
  • user_id
  • 文字

根据 type 字段的基数,您需要测试 GSI 是否真的有用。

如果 GSI 没有什么好处,而您需要更高的性能,另一种选择是将 typenote_id 数组一起存储在单独的表中。请注意此项目的 400k 项目限制,以及您需要执行另一个查询才能获取注释的text


使用此表结构和 GSI,您可以对所需信息进行一次查询,而不是在有两个表时进行两次查询。

当然,您最了解您的数据 - 最好从您认为最好的开始,然后对其进行测试以确保它符合您的要求。 DynamoDB 按预置吞吐量 + 存储的索引数据量定价,因此创建具有许多属性项目的“胖”索引,如上所述,如果有大量数据,则执行两个查询并存储更少的索引数据可能会变得更具成本效益.

【讨论】:

  • 谢谢,似乎是正确的方法。由于成本以及如何正确处理为它们提供的吞吐量,我总是对使用 GSI 犹豫不决,但我认为您是对的。
  • 是的,这是一个棘手的问题。首先,保持为写入配置的吞吐量与表相同,并根据需要进行调整。根据 GSI 的使用频率,表中的读数可能更大或更小。
【解决方案2】:

我会使用 user_id 作为你的主分区(哈希)键和 note_id 作为你的主范围(排序)键。

您已经注意到,在理想情况下,每个分区键都以相同的规律访问以优化性能,请参阅Design For Uniform Data Access Across Items In Your Tables。只要您拥有大量定期登录的用户,使用 user_id 就完全没问题。事实上,AWS 特别鼓励这个选项(请参阅上面链接中的“选择分区键”表)。

这种方法也将使您的应用程序代码比您的替代方法简单得多。

然后您有第二个选择,即是否为您的 get notes by type 查询应用全局二级索引。与主键不同,GSI 键不需要是唯一的(请参阅AWS GSI guide,因此我建议您只需使用 type 作为 GSI 分区键而不使用范围键。

使用 GSI 的明显优势是执行笔记类型查询时结果更快。但是,您也应该注意缺点。 GSI 与您的表相比具有单独的吞吐量限额,因此除了表吞吐量外,您还需要配置此限额(额外费用)。如果您没有为您的 GSI 提供足够的读取单元,它最终可能会比在您的表上进行扫描要慢。如果您没有提供足够的写入单元,即使您的表有足够的写入单元,您的表写入也可能会受到限制。

此外,AWS 警告说 GSI 是异步更新的(通常在几分之一秒内,但可能会更长)。这意味着如果表写入和索引读取非常接近,则对 GSI 的查询可能会返回“错误”结果。如果这是一个问题,您需要在应用程序代码中处理它。

【讨论】:

    【解决方案3】:

    我认为这是 2 张桌子。注释表上带有 GSI 的用户和注释。不知道你还能怎么做。使用 userId 作为主键和 note_id 作为排序键要求您只有在知道 user_id 和 note_id 时才能检索元素。使用 DynamoDB,如果您不扫描,则必须满足主键中的所有元素,因此分区和排序(如果有的话)。下面是我将如何做到这一点。

    获取某个用户的所有笔记

    当用户创建注释时,我会将其添加到用户注释属性中的用户表中。当您想要获取所有用户的注释时,请检索该用户并访问存储在那里的 note_ids 数组/列表。

    { userId: xxx,
      notes: [ note_id_1,note_id_2,note_id_3]
    }
    

    获取特定注释

    以 node_id 作为主键的 notes 表会很容易。

    {
    noteId: XXXX,
    note: "sfsfsfsfsfsf",
    type: "standard_note"
    }
    

    获取某种类型的所有笔记(使用较少的查询) 为此,我会在 notes 表上使用 GSI,并将 "note_type" 和 note_id 的属性投影到它上面。

    更新

    您可以使用一张桌子和一个 GSI 来完成此操作(请参阅下面的两个答案了解如何),但我不会这样做。您的数据模型如此简单,为什么它比用户和注释更复杂。

    【讨论】:

    • 感谢您的建议,但在这种情况下,如果我需要显示用户所有笔记的列表,并且我需要在该列表中显示该笔记的一些属性(例如note 类型),我需要对 notes 数组的每个 note 做一个额外的查询,对吧?
    • @yabune 这将由您决定。如果每个笔记中的信息量少于您可以选择存储完整的笔记。是的,这意味着复制您的数据,但这就是权衡。如果您尝试构建分布式表的 SQL 样式结构,您将进行额外的往返。复制数据以解决缺少连接的问题是 AWS 推荐的另一种模式。这是一个权衡。
    • @yabune 查看我之前的评论...我还添加了更新。如果你真的想用一张桌子来做这件事是可能的。请参阅其他两个答案。
    猜你喜欢
    • 1970-01-01
    • 2018-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多