我一直在阅读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)
我想执行以下查询:
calendarId = x
和ownerId = y
startTimestamp is between x and y
和calendarId = z
DynamoDB文档似乎强烈建议避免在此处使用事件的ID作为分区/排序键,那么推荐的架构是什么?
答案 0 :(得分:10)
这是一个每个人在开始使用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。然而,根据我的经验,人们对这个问题太过痴迷,如果您阅读我刚刚链接的文章(您也链接过),这就不足为奇了。
请记住,我们在Query中使用分区键来快速获取数据,并避免定期扫描。有些人过于专注,使他们的分区访问完全统一,并最终得到一个他们无法快速查询的表。
我想参考Best Practices for Tables guide。尤其是表格中用户ID是一个很好的分区键,因此很多用户经常访问您的应用程序。 (它实际上说你有很多用户 - 这是不正确的,表的大小是无关紧要的。)
它在统一访问和能够为您的应用程序使用直观,自然的查询之间取得平衡,但我所说的是,如果您是DyanmoDB的新手,正确的答案可能是设计您的表基于直观访问。在您成功完成此操作后,请考虑统一访问和热分区,但请记住访问不必完全一致。有各种设计模式可以实现直观和统一访问,但对于那些刚开始使用的设备模式可能很复杂,并且在很多情况下,如果他们过于关注统一访问的想法,可能会阻止使用DynamoDB的人。
大多数应用程序都有用户。对于大多数查询,在大多数应用程序中,您要做的最常见的查询是为用户获取数据。因此,大多数应用程序的主分区键的第一个选项通常是用户ID。没关系,只要你没有一些非常高的用户和许多从未登录的用户。
另一个提示。如果您的表名为vegetables,您的主分区键可能是蔬菜ID。如果您的表名为shoes,则您的主分区键可能是鞋号。
大多数应用程序将为每个用户(或蔬菜或鞋子)提供许多项目。主键必须是唯一的。一个好的选择通常是添加日期范围(排序)键 - 可能是项目创建的日期时间。然后,它按创建日期对用户分区内的项目进行排序,并且还为每个项目提供唯一的复合主键(即散列键+范围键)。使用生成的UUID作为范围键也没关系,你不会使用它给你的顺序,但是你可以为每个用户提供很多项目并仍然使用查询功能。
啊哈!但我可以让我的分区键完全随机,然后应用索引与我真正想要查询的属性的分区键。这样我就可以获得统一访问和快速直观查询。
可悲的是没有。索引具有自己的吞吐量和分区,与构建索引的表分开。想象一下索引作为一个完整的新表 - 这基本上就是它们的本质。 索引不适用于不均匀的分区访问。
主键
哈希键:事件ID
范围键:无
全球二级索引
哈希键:日历ID
范围键:startTimestamp
假设统一访问事件ID,它将是一个很好的哈希键。您真的需要描述如何分发数据以进行更多讨论。其他可以发挥作用的是您希望查询工作的速度以及您愿意支付多少费用(例如二级索引很昂贵)。
您的疑问:
按ID
获取活动GetItem使用事件ID
获取calendarId = x和ownerId = y
的所有事件按GSI分区键查询,在ownerId上添加条件
获取startTimestamp介于x和y之间以及calendarId = z的所有事件
按GSI分区键查询,在范围键上添加条件