对于典型的 crud 应用程序,推荐的 dynamo 索引架构是啥?
Posted
技术标签:
【中文标题】对于典型的 crud 应用程序,推荐的 dynamo 索引架构是啥?【英文标题】:What's the recommended index schema for dynamo for a typical crud application?对于典型的 crud 应用程序,推荐的 dynamo 索引架构是什么? 【发布时间】:2018-06-21 12:55:30 【问题描述】:我一直在阅读some DynamoDB index docs,他们让我比任何事情都更加困惑。让我们用一个具体的例子来澄清一下。
我有一个简单的日历应用程序,其中有一个events
表。以下是我的专栏:
id: guid,
name: string,
startTimestamp: integer,
calendarId: guid (foreign key in a traditional RDBMS model)
ownerId: guid (foreign key in a traditional RDBMS model)
我想执行如下查询:
按 ID 获取事件 获取calendarId = x
和ownerId = y
的所有事件
获取startTimestamp is between x and y
和calendarId = z
的所有事件
DynamoDB 文档似乎强烈建议避免在此处使用事件 ID 作为分区/排序键,那么推荐的架构是什么?
【问题讨论】:
【参考方案1】:这是每个人在开始使用 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 分区键查询,在范围键上添加条件
【讨论】:
您的答案中列出了一些不完全正确或不再正确的内容:即。 “DyanmoDB 为您的每个哈希键创建一个分区。” - 这不是真的;从来都不是真的; “DynamoDB 获取您购买的所有吞吐量,并将其均匀地分布在所有表分区上” - 有一点需要注意,尤其是现在有了自适应能力,它变得比以前更加模糊了 谢谢 - 更新了答案。我没有根据您的第二点更改答案,而是添加了一个链接,以便人们可以阅读更多内容。以上是关于对于典型的 crud 应用程序,推荐的 dynamo 索引架构是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Dynamics CRM 2015/2016 Web API:重客户端的CRUD操作