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

16 浏览
0 Comments

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

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

    \n

  • id(uuid)
  • \n

  • lastModifiedAt(时间戳)
  • \n

  • name
  • \n

  • latitude
  • \n

  • longitude
  • \n

\n我目前正在通过lastModifiedAt进行扫描和过滤,其中lastModifiedAt大于应用程序上次刷新的日期,但我想这种方法会变得很昂贵。\n我能找到的最好的解决方案是使用lastModifiedAt作为范围创建一个全局二级索引,但没有明显的哈希键可以使用。\n在需要使用全局二级索引进行范围查询时,但没有明显的哈希键时,有什么最佳实践?或者,如果全面扫描是唯一的选择,有没有任何降低成本的最佳实践?

0
0 Comments

问题的原因是:DynamoDB中的查询需要一个明确的哈希键(hash key),而对于按日期(范围键)查询的情况没有明显的哈希键可用。

解决方法是:可以将时间戳的“日期”部分作为哈希键,将完整的时间戳作为范围键。但是,由于哈希键要求一个eq条件,那么是否需要对自应用上次刷新的时间戳之后的每一天执行一个查询呢?这似乎比全表扫描更加昂贵。

另一个解决方法是:可以将时间戳的“年-月”部分作为哈希键。相比于使用“日期”部分,这将大大减少在某人在一年内第一次打开应用程序时所需的查询次数。在那个时候,范围键似乎也变得无关紧要了,因为通过哈希键查询将带来自上次刷新以来更新的所有项目。

0
0 Comments

问题的原因是,作者在查询DynamoDB时使用了日期作为范围键(range key),但没有明显的哈希键(hash key)。作者试图使用day+timestamp作为哈希+范围键,但这种方法可能导致键不唯一,因此无法正确查询数据。另外,如果只使用day作为哈希键,需要进行大量的查询才能获取自上次刷新日期以来的每天的结果。

为了解决这个问题,作者采取了以下方法:

1. 创建一个全局二级索引(Global Secondary Index, GSI),将哈希键设置为YearMonth(例如,201508),将范围键设置为id。

2. 针对自上次刷新日期以来的每个月,多次查询GSI。每个查询都需要使用筛选器过滤出lastModifiedAt > [给定时间戳]的结果。

作者在回答中提到了其他一些需要考虑的因素。感谢作者的分享和解决方案。

在评论中,某些情况下GSI不需要唯一性,提供了一个相关的AWS文档链接作为参考。

以上是解决"如何在DynamoDB中按日期(范围键)查询,而没有明显的哈希键?"问题的方法。

0
0 Comments

在使用DynamoDB时,如果想要按日期(范围键)查询,但没有明显的哈希键,可能会遇到一些问题。在这种情况下,使用全局二级索引(Global Secondary Index,GSI)可能符合需求,但是如果将时间戳相关信息作为哈希键的一部分,很可能会创建出所谓的“热分区”(Hot Partition),这是极其不可取的。热分区会导致访问不均衡,最近的项目的检索频率远高于旧项目。这不仅会影响性能,还会使解决方案的成本效益降低。

根据文档中的一些细节可以看出:

"例如,如果一个表只有很少数量的强烈访问的分区键值,甚至可能只有一个非常频繁使用的分区键值,请求流量将集中在少数分区上,可能仅仅是一个分区。如果工作负载不平衡,即不成比例地集中在一个或几个分区上,请求将无法达到整体预留吞吐量水平。为了充分利用DynamoDB的吞吐量,请创建分区键具有大量不同值的表,并且值是均匀地、尽可能随机地请求的"。

基于上述内容,id似乎是哈希键(也称为分区键)的一个很好的选择。至于GSI键,在分区方面的工作方式与哈希键相同。另外,通过提供完整的主键来检索数据会大大优化性能,因此我们应该尽可能找到提供完整主键的解决方案。

我建议创建单独的表来存储根据最近更新时间生成的主键。您可以根据最适合您的用例的粒度将数据分割成表。例如,假设您想按天分割更新:

a. 每天更新的数据可以存储在以下命名约定的表中:updates_DDMM

b. updates_DDMM表只包含id(另一个表的哈希键)

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

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

ii. 最后,通过提交BatchGetItem请求使用所有获取到的哈希键从主表(包含lat/lng、name等)中获取记录。BatchGetItem非常快速,可以像没有其他操作一样完成工作。

有人可能会说创建附加表会增加整体解决方案的成本... 嗯,使用GSI时,您本质上是复制表(如果您投影所有字段),并为所有~2k条记录增加了额外的成本,无论它们是最近更新的还是不是...

这似乎与直觉相悖,但实际上,这是处理时间序列数据的最佳实践(来自AWS DynamoDB文档):

"[...]应用程序可能会在表中的所有项目上显示不均衡的访问模式,其中最新的客户数据更相关,您的应用程序可能更频繁地访问最新的项目,并且随着时间的推移,这些项目的访问频率会降低,最终较旧的项目很少被访问。如果这是已知的访问模式,则可以在设计表模式时考虑此模式。您可以使用多个表存储这些项目。例如,您可以创建表以存储每月或每周的数据。对于存储来自最新月或最新周的数据的表,数据访问率很高,请求更高的吞吐量,并且对于存储较旧数据的表,可以降低吞吐量并节省资源。您可以通过简单地删除表来删除旧项目。您还可以选择将这些表备份到其他存储选项,例如亚马逊简单存储服务(Amazon S3)。删除整个表要比逐个删除项目更高效,因为您需要执行与放置操作一样多的删除操作,这实际上会使写入吞吐量增加一倍。"

根据以上解决方案,感谢您的建议。这让我想知道在访问基于时间的数据的复杂性之下,RDS是否是更好的解决方案。

还要注意,BatchGetItem的限制是100个项目。

有人指出热分区的问题已经过时,因为有自适应容量。此外,Dynamo还有适用于时间序列数据的设计模式,其中主键是时间戳。

很明显,DynamoDB是为OLTO和OLAP而设计的。

0