这是每个人在开始使用 DynamoDB 时(实际上是在使用过 DynamoDB 时)都会遇到的问题。
定价和吞吐量
让我们从 DynamoDB 是如何priced 开始的(它的相关性 - 老实说)。暂时忽略免费层,您每月为静态数据支付每 GB 0.25 美元。您还需要为每个写入容量单位 (WCU) 每月支付 0.47 美元,每个读取容量单位 (RCU) 每月支付 0.09 美元。 Throughput 是表上 WCU 和 RCU 的数量。您必须在表上预先指定吞吐量 - 您可以在表上执行的写入和读取量受您的吞吐量规定的限制。支付更多的钱,您每秒可以进行更多的读取和写入。有关 DynamoDB 如何对表进行分区的详细信息,请参阅 this answer。
按键
现在我们需要考虑表分区。表必须有一个主键。主键必须具有哈希键(也称为分区键),并且可以选择具有排序键(也称为范围键)。 DynamoDB 根据您的哈希键值创建 partitions。在分区键值内,数据按范围键排序(如果您指定了一个)。
数据访问
如果您有准确的主键(如果有,则为哈希键和范围键),您可以使用GetItem 立即访问项目。如果您有多个项目要获取,您可以使用BatchGetItem。
DynamoDB 只能通过两种方式“搜索”数据。 Query 可以一次调用只能从一个分区中获取数据,因为它使用分区键(以及可选的排序键)速度很快。 Scan 总是评估表中的每个项目,因此它通常很慢并且不能很好地在大型表上扩展。
吞吐量分布
这就是有趣的地方。 DynamoDB 会占用您购买的所有吞吐量和evenly spreads it over all of you table partitions。假设您的表上有 10 个 WCU 和 10 个 RCU,以及 5 个分区,这意味着每个分区有 2 个 WCU 和 2 个 RCU。如果您均匀地访问每个分区,那很好,您可以使用您购买的所有吞吐量。但想象一下,您只访问过一个分区。现在您已经购买了 10 个 WCU 和 RCU,但您只使用了 2 个。您的表将比您想象的要慢得多。一种选择是购买更多的吞吐量,这会奏效,但对大多数工程师来说可能不太满意。
统一访问与自然访问
基于以上我们知道我们想要design a table where each partition gets accessed evenly。但是,根据我的经验,人们对此过于关注,如果您阅读我刚刚链接的文章(您也链接了该文章),这并不奇怪。
请记住,我们在查询中使用分区键来快速获取数据,并避免定期扫描。有些人过于专注于使他们的分区访问完全统一,最终得到一个他们无法快速查询的表。
答案
我喜欢参考Best Practices for Tables guide。尤其是表中显示用户 ID 是一个很好的分区键,只要许多用户定期访问您的应用程序。 (它实际上说明了您在哪里有很多用户 - 这是不正确的,表的大小无关紧要)。
它在统一访问和能够为您的应用程序使用直观、自然的查询之间取得平衡,但我的意思是,如果您是 DyanmoDB 的新手,正确的答案可能是设计您的表基于直观的访问。成功完成之后,请考虑统一访问和热分区,但请记住访问不必完全统一。有多种设计模式可以实现直观和统一的访问,但对于刚起步的人来说这些可能会很复杂,而且在许多情况下,如果人们过于关注统一访问的想法,可能会阻碍使用 DynamoDB 的人。
提示
大多数应用程序都会有用户。对于大多数查询,在大多数应用程序中,您将执行的最常见查询是为用户获取数据。因此,大多数应用程序的主分区键的第一个选项通常是用户 ID。没关系,只要您没有几个点击率非常高的用户和许多从不登录的用户。
另一个提示。如果您的表称为蔬菜,则您的主分区键可能是蔬菜 ID。如果您的表名为 shoes,则您的主分区键可能是 shoe id。
大多数应用程序将为每个用户(或蔬菜或鞋子)提供许多项目。主键必须是唯一的。一个不错的选择通常是添加日期范围(排序)键 - 可能是创建项目的日期时间。然后按创建日期对用户分区中的项目进行排序,并为每个项目提供唯一的复合主键(即散列键 + 范围键)。使用生成的 UUID 作为范围键也很好,您不会使用它为您提供的排序,但是您可以为每个用户拥有许多项目,并且仍然使用 Query 功能。
索引不是解决方案
啊哈!但是我可以让我的分区键完全随机,然后使用我真正想要查询的属性的分区键应用索引。这样我就可以获得统一的访问权限和快速直观的查询。
遗憾的是没有。索引有自己的吞吐量和分区,独立于构建索引的表。想象一下索引是一个全新的表——它们基本上就是这样。 索引不能解决不均匀的分区访问。
最后 - 你的架构
主键
哈希键:事件 ID
范围键:无
全球二级索引
哈希键:日历 ID
范围键:startTimestamp
假设事件 ID 被统一访问,它将是一个很好的哈希键。您确实需要描述您的数据是如何分布的,以便更多地讨论这一点。其他需要考虑的因素是您希望查询以多快的速度运行以及您愿意支付多少费用(例如二级索引很昂贵)。
您的疑问:
按 ID 获取事件
GetItem 使用事件 ID
获取 calendarId = x 和 ownerId = y 的所有事件
通过GSI分区键查询,在ownerId上添加条件
获取 startTimestamp 在 x 和 y 之间且 calendarId = z 的所有事件
通过 GSI 分区键查询,在范围键上添加条件