如何通过日期(范围键)查询DynamoDB,而没有明显的哈希键?
如何通过日期(范围键)查询DynamoDB,而没有明显的哈希键?
我需要将iOS应用程序中的本地数据与DynamoDB表中的数据保持同步。DynamoDB表大约有2K行数据,只有一个哈希键(id
),以及以下属性:\n
- \n
id
(uuid)lastModifiedAt
(时间戳)name
latitude
longitude
\n
\n
\n
\n
\n
\n我目前正在通过lastModifiedAt
进行扫描和过滤,其中lastModifiedAt
大于应用程序上次刷新的日期,但我想这种方法会变得很昂贵。\n我能找到的最好的解决方案是使用lastModifiedAt
作为范围创建一个全局二级索引,但没有明显的哈希键可以使用。\n在需要使用全局二级索引进行范围查询时,但没有明显的哈希键时,有什么最佳实践?或者,如果全面扫描是唯一的选择,有没有任何降低成本的最佳实践?
问题的原因是,作者在查询DynamoDB时使用了日期作为范围键(range key),但没有明显的哈希键(hash key)。作者试图使用day+timestamp作为哈希+范围键,但这种方法可能导致键不唯一,因此无法正确查询数据。另外,如果只使用day作为哈希键,需要进行大量的查询才能获取自上次刷新日期以来的每天的结果。
为了解决这个问题,作者采取了以下方法:
1. 创建一个全局二级索引(Global Secondary Index, GSI),将哈希键设置为YearMonth(例如,201508),将范围键设置为id。
2. 针对自上次刷新日期以来的每个月,多次查询GSI。每个查询都需要使用筛选器过滤出lastModifiedAt > [给定时间戳]的结果。
作者在回答中提到了其他一些需要考虑的因素。感谢作者的分享和解决方案。
在评论中,某些情况下GSI不需要唯一性,提供了一个相关的AWS文档链接作为参考。
以上是解决"如何在DynamoDB中按日期(范围键)查询,而没有明显的哈希键?"问题的方法。
在使用DynamoDB时,如果想要按日期(范围键)查询,但没有明显的哈希键,可能会遇到一些问题。在这种情况下,使用全局二级索引(Global Secondary Index,GSI)可能符合需求,但是如果将时间戳相关信息作为哈希键的一部分,很可能会创建出所谓的“热分区”(Hot Partition),这是极其不可取的。热分区会导致访问不均衡,最近的项目的检索频率远高于旧项目。这不仅会影响性能,还会使解决方案的成本效益降低。
根据文档中的一些细节可以看出:
"例如,如果一个表只有很少数量的强烈访问的分区键值,甚至可能只有一个非常频繁使用的分区键值,请求流量将集中在少数分区上,可能仅仅是一个分区。如果工作负载不平衡,即不成比例地集中在一个或几个分区上,请求将无法达到整体预留吞吐量水平。为了充分利用DynamoDB的吞吐量,请创建分区键具有大量不同值的表,并且值是均匀地、尽可能随机地请求的"。
基于上述内容,id似乎是哈希键(也称为分区键)的一个很好的选择。至于GSI键,在分区方面的工作方式与哈希键相同。另外,通过提供完整的主键来检索数据会大大优化性能,因此我们应该尽可能找到提供完整主键的解决方案。
我建议创建单独的表来存储根据最近更新时间生成的主键。您可以根据最适合您的用例的粒度将数据分割成表。例如,假设您想按天分割更新:
a. 每天更新的数据可以存储在以下命名约定的表中:updates_DDMM
b. updates_DDMM
表只包含id
(另一个表的哈希键)
假设最新的应用程序刷新日期是2天前(04/07/16),您需要获取最近的记录,然后需要:
i. 扫描表updates_0504
和updates_0604
以获取所有哈希键。
ii. 最后,通过提交BatchGetItem
请求使用所有获取到的哈希键从主表(包含lat/lng、name等)中获取记录。BatchGetItem
非常快速,可以像没有其他操作一样完成工作。
有人可能会说创建附加表会增加整体解决方案的成本... 嗯,使用GSI时,您本质上是复制表(如果您投影所有字段),并为所有~2k条记录增加了额外的成本,无论它们是最近更新的还是不是...
这似乎与直觉相悖,但实际上,这是处理时间序列数据的最佳实践(来自AWS DynamoDB文档):
"[...]应用程序可能会在表中的所有项目上显示不均衡的访问模式,其中最新的客户数据更相关,您的应用程序可能更频繁地访问最新的项目,并且随着时间的推移,这些项目的访问频率会降低,最终较旧的项目很少被访问。如果这是已知的访问模式,则可以在设计表模式时考虑此模式。您可以使用多个表存储这些项目。例如,您可以创建表以存储每月或每周的数据。对于存储来自最新月或最新周的数据的表,数据访问率很高,请求更高的吞吐量,并且对于存储较旧数据的表,可以降低吞吐量并节省资源。您可以通过简单地删除表来删除旧项目。您还可以选择将这些表备份到其他存储选项,例如亚马逊简单存储服务(Amazon S3)。删除整个表要比逐个删除项目更高效,因为您需要执行与放置操作一样多的删除操作,这实际上会使写入吞吐量增加一倍。"
根据以上解决方案,感谢您的建议。这让我想知道在访问基于时间的数据的复杂性之下,RDS是否是更好的解决方案。
还要注意,BatchGetItem的限制是100个项目。
有人指出热分区的问题已经过时,因为有自适应容量。此外,Dynamo还有适用于时间序列数据的设计模式,其中主键是时间戳。
很明显,DynamoDB是为OLTO和OLAP而设计的。