对于典型的crud应用程序,dynamo的推荐索引模式是什么?

Jim*_*Jim 7 amazon-dynamodb

我一直在阅读一些DynamoDB索引文档,他们让我更加困惑.让我们用一个具体的例子来清除空气.

我有一个简单的日历应用程序,我有一张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)
Run Code Online (Sandbox Code Playgroud)

我想执行以下查询:

  • 按ID获取活动
  • 在哪里calendarId = x和所有活动ownerId = y
  • 在哪里startTimestamp is between x and y和所有活动calendarId = z

DynamoDB文档似乎强烈建议避免在此处使用事件的ID作为分区/排序键,那么推荐的架构是什么?

F_S*_*O_K 11

这是一个每个人在开始使用DynamoDB时(实际上他们都有经验)时都会遇到的问题.

定价和吞吐量

让我们从DynamoDB的定价方式开始(相关 - 诚实).暂时忽略免费套餐,您每月每GB支付0.25美元的静态数据.您还需支付每月写入容量单位(WCU)0.47美元和每月读取容量单位(RCU)0.09美元.吞吐量是您桌面上的WCU和RCU的数量.您必须预先在表上指定吞吐量 - 您可以在表上执行的写入和读取量受吞吐量提供的限制.支付更多的钱,你可以每秒做更多的读写操作.有关DynamoDB如何分区表的详细信息,请参阅此答案.

按键

现在我们需要考虑表分区.表必须具有主键.主键必须具有散列键(也称为分区键),并且可以可选地具有排序键(也称为范围键).DynamoDB 根据您的哈希键值创建分区.在分区键值内,如果您指定了数据,则按范围键对数据进行排序.

数据访问

如果您有确切的主键(如果有主键,则为哈希键和范围键),您可以使用GetItem立即访问项目.如果要获取多个项目,可以使用BatchGetItem.

DynamoDB只能以两种方式"搜索"数据.一个查询可以只从一个分区取的数据在一个调用,因为它使用分区键(也可以选择排序关键字),它是快速.一个扫描的结果总是在表中的每个项目,所以它通常很慢,并大表上不能很好地扩展.

吞吐量分布

这是有趣的地方.DynamoDB获取您购买的所有吞吐量,并将其均匀分布在所有表分区上.想象一下,您的桌子上有10个WCU和10个RCU,以及5个分区,这意味着每个分区有2个WCU和2个RCU.如果您均匀地访问每个分区,则可以使用所有已购买的吞吐量.但想象你访问一个分区.现在你已经购买了10个WCU和RCU但是你只使用了2.你的桌子会比你想象的慢得多.一种选择是购买更多的吞吐量,这将是有效的,但对大多数工程师来说可能并不十分令人满意.

统一访问v自然访问

基于以上所述,我们知道我们想要设计一个表,其中每个分区均匀访问.然而,根据我的经验,人们对此过于担心,如果您阅读我刚刚链接的文章(您也链接过),这就不足为奇了.

请记住,我们在Query中使用分区键来快速获取数据,并避免定期扫描.有些人过于专注,使他们的分区访问完全统一,并最终得到一个他们无法快速查询的表.

答案

我想参考表格最佳实践指南.特别是表中用户ID是一个很好的分区键的表,因此许多用户经常访问您的应用程序.(它实际上说你有很多用户 - 这是不正确的,表的大小是无关紧要的).

它在统一访问和能够为您的应用程序使用直观,自然的查询之间取得平衡,但我所说的是,如果您是DyanmoDB的新手,正确的答案可能是基于直观访问设计您的表.成功完成后,请考虑统一访问和热分区,但请记住访问不必完全统一.有各种设​​计模式可以实现直观和统一访问,但对于那些刚刚开始的人来说,这些模式可能很复杂,并且在很多情况下,如果人们过于关注统一访问的想法,可能会阻止使用DynamoDB的人.

提示

大多数应用程序都有用户.对于大多数查询,在大多数应用程序中,您要做的最常见的查询是为用户获取数据.因此,大多数应用程序的主分区键的第一个选项通常是用户ID.这很好,只要你没有一些非常高的用户和许多从未登录的用户.

另一个提示.如果您的表名为vegetables,您的主分区键可能是蔬菜ID.如果您的表被称为鞋子,您的主分区键可能是鞋子ID.

大多数应用程序将为每个用户(或蔬菜或鞋)提供许多项目.主键必须是唯一的.一个好的选择通常是添加日期范围(排序)键 - 可能是项目创建的日期时间.然后,它按创建日期对用户分区内的项目进行排序,并为每个项目提供唯一的复合主键(即散列键+范围键).使用生成的UUID作为范围键也没关系,你不会使用它给你的顺序,但是你可以为每个用户提供许多项目并仍然使用查询功能.

索引不是解决方案

啊哈!但我可以让我的分区键完全随机,然后应用索引与我真正想要查询的属性的分区键.这样我就可以获得统一访问和快速直观查询.

可悲的是没有.索引具有自己的吞吐量和分区,与构建索引的表分开.想象索引作为一个完整的新表 - 这基本上就是它们的本质.索引不是解决不均匀分区访问的问题.

最后 - 你的架构

首要的关键

哈希键:事件ID

范围键:无

全球二级指数

哈希键:日历ID

范围键:startTimestamp

假设统一访问事件ID,它将是一个很好的哈希键.您真的需要描述如何分发数据以进行更多讨论.其他可以发挥作用的是您希望查询工作的速度以及您愿意支付的费用(例如,二级索引很昂贵).

而你的疑问:

按ID获取活动

GetItem使用事件ID

获取calendarId = x和ownerId = y的所有事件

通过GSI parition键查询,在ownerId上添加一个条件

获取startTimestamp在x和y之间以及calendarId = z之间的所有事件

通过GSI分区键查询,在范围键上添加条件

  • 您的答案中列出了一些不完全正确或不再正确的内容:即."DyanmoDB为每个哈希键创建一个分区." - 这不是真的; 它从来都不是真的; "DynamoDB获取你已经购买的所有吞吐量,并将其均匀地分布在你所有的表分区上" - 有点警告,特别是现在有了适应能力,它变得比以前更加模糊了. (2认同)