如何按日期(范围键)查询DynamoDB,没有明显的哈希键?

时间:2016-03-12 20:58:53

标签: amazon-web-services amazon-dynamodb aws-sdk

我需要将iOS应用上的本地数据与DynamoDB表中的数据保持同步。 DynamoDB表是大约2K行,只有一个散列键(id),以及以下属性:

  • id(uuid)
  • lastModifiedAt(时间戳)
  • name
  • latitude
  • longitude

我目前正在lastModifiedAt进行扫描和过滤,其中lastModifiedAt大于应用的上次刷新日期,但我想这会变得很贵。

我能找到的最好answer是添加一个lastModifiedAt作为范围的全局二级索引,但GSI没有明显的哈希键。

当需要使用GSI按范围查询时,最佳做法是什么,但没有明显的散列键?或者,如果完全扫描是唯一选项,是否有任何最佳做法可以保留降低成本?

3 个答案:

答案 0 :(得分:27)

虽然Global Secondary Index似乎符合您的要求,但任何将timestamp相关信息作为Hash Key的一部分包含在内的尝试很可能会创建所谓的“热门分区”,非常不受欢迎。

将发生不均匀访问,因为将以比旧旧项目更频繁的方式检索最新项目。这不仅会影响您的性能,还会降低您的解决方案的成本效益。

请参阅文档中的一些详细信息:

  

例如,如果一个表具有非常少量的大量访问   分区键值,甚至可能是一个非常大量使用的键值   分区键值,请求流量集中在少数   分区 - 可能只有一个分区。如果工作量是   严重失衡,意味着它不成比例地集中在   一个或几个分区,请求将无法实现整体   预配置吞吐量水平。要充分利用DynamoDB   吞吐量,创建分区键具有较大数字的表   不同的值,并且要求相当一致的值,如   尽可能随机。

根据陈述的内容,id似乎确实是您Hash Key(又名Partition Key)的不错选择,我不会因为GSI键工作而改变它与分区相同的方式。作为单独的注释,当您通过提供整个Primary Key来检索数据时,性能会得到高度优化,因此我们应该尽可能找到提供该解决方案的解决方案。

我建议创建单独的表来存储主键,具体取决于它们的更新时间。您可以根据最适合您的用例的粒度将数据分段到表中。例如,假设您希望按天分段更新:

一个。您的每日更新可以存储在具有以下命名约定的表中:updates_DDMM

updates_DDMM表只有id个(另一个表的哈希键)

现在说最新的应用刷新日期是2天前(04/07/16),您需要获取最近的记录,然后您需要:

我。扫描表updates_0504updates_0604以获取所有哈希键。

II。最后通过提交带有所有获得的散列键的BatchGetItem来从主表(包含lat / lng,名称等)中获取记录。

BatchGetItem非常快,并且可以像其他操作一样完成工作。

有人可能会争辩说,创建额外的表会增加整体解决方案的成本......好吧,GSI你基本上会复制你的表(如果你预测所有字段)并为所有字段增加额外费用〜2k记录,最近是否更新......

这似乎是反直觉创建这样的表,但它实际上是处理时间序列数据时的最佳实践(来自AWS DynamoDB文档):

  

[...]应用程序可能会在所有项目中显示不均匀的访问模式   在表格中,最新的客户数据与您的相关性更高   应用程序可能会更频繁地访问最新项目   通过这些项目访问较少,最终旧项目   很少访问。如果这是一种已知的访问模式,您可以接受它   在设计表模式时要考虑到这一点。代替   将所有项目存储在一个表中,您可以使用多个表   存储这些物品。例如,您可以创建要存储的表   每月或每周数据。对于存储最新数据的表   月或周,数据访问率高,请求更高   吞吐量和存储旧数据的表,您可以拨打   吞吐量和节省资源。

     

您可以通过在一个表中存储“热”项来节省资源   更高的吞吐量设置,以及另一个表中的“冷”项   较低的吞吐量设置您只需删除即可删除旧项目   表格。您可以选择将这些表备份到其他存储   Amazon Simple Storage Service(Amazon S3)等选项。正在删除   整个表比删除项目更有效   一个接一个,它基本上使您的写吞吐量翻倍   与put操作一样多的删除操作。

来源: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GuidelinesForTables.html

我希望有所帮助。问候。

答案 1 :(得分:9)

虽然D.Shawley的回答帮助我指出了正确的方向,但它错过了GSI的两个考虑因素:

  1. 哈希+范围必须是唯一的,但是日期+时间戳(他推荐的方法)不一定是唯一的。
  2. 通过仅使用日期作为哈希,我需要使用大量查询来获取自上次刷新日期(可能是几个月或几年前)以来每天的结果。
  3. 因此,这是我采取的方法:

    • 创建全局二级索引(GSI),散列密钥为YearMonth(例如201508),范围为id
    • 多次查询GSI,自上次刷新日期起每个月查询一次。查询也会使用lastModifiedAt > [given timestamp]进行过滤。

答案 2 :(得分:6)

您可以使用时间戳的“日期”部分作为哈希值,并使用完整时间戳作为范围。