【问题标题】:How to query DynamoDB by date (range key), with no obvious hash key?如何按日期(范围键)查询 DynamoDB,没有明显的哈希键?
【发布时间】:2016-06-28 01:22:27
【问题描述】:

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

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

    尽管Global Secondary Index 似乎符合您的要求,但任何尝试将timestamp 相关信息作为Hash Key 的一部分都将很可能会创建所谓的“热分区”,这是非常不可取的。

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

    查看文档中的一些详细信息:

    例如,如果一个表有非常少量的重度访问 分区键值,甚至可能是一个非常频繁使用的 分区键值,请求流量集中在少数 分区数——可能只有一个分区。如果工作量是 严重不平衡,这意味着它不成比例地专注于 一个或几个分区,请求不会实现整体 预置的吞吐量级别。充分利用 DynamoDB 吞吐量,创建分区键有大量的表 不同的值,并且值的要求相当一致,因为 尽可能随机。

    根据所述内容,id 似乎确实是您的Hash Key(又名Partition Key)的不错选择,我不会更改这一点,因为 GSI 密钥的工作方式与分区。作为单独的说明,当您通过提供整个Primary Key 检索数据时,性能得到了高度优化,因此我们应该尽可能地尝试找到一个提供该功能的解决方案。

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

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

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

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

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

    二。最后通过提交带有所有获得的哈希键的BatchGetItem,从主表中获取记录(包含纬度/经度、名称等)。

    BatchGetItem 速度超级快,可以像其他操作一样完成这项工作。

    有人可能会争辩说,创建额外的表格会增加您的整体解决方案的成本......好吧,使用GSI,您实际上是在复制您的表格(以防您投影所有字段)并为所有 ~2k 增加额外的成本记录,是否最近更新...

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

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

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

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

    我希望这会有所帮助。问候。

    【讨论】:

    • 这是一个很好的建议。感谢您对此的深思熟虑。考虑到访问基于时间的数据的复杂性,这让我想知道 RDS 是否是一个更好的解决方案。
    • 请注意 BatchGetItem 仅限于 100 个项目。
    • 这是一个很好的评论,但“热分区”问题是outdated with adaptive capacity。请注意,还有 Dynamo design patterns for time-series data,PK 是时间戳。
    【解决方案2】:

    虽然 D.Shawley 的回答帮助我指出了正确的方向,但它忽略了 GSI 的两个注意事项:

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

    因此,这是我采取的方法:

    • 创建了一个全局二级索引(GSI),哈希键为YearMonth(例如201508),范围为id
    • 多次查询 GSI,自上次刷新日期以来每个月查询一次。查询也使用lastModifiedAt > [given timestamp] 过滤。

    【讨论】:

    【解决方案3】:

    您可以使用时间戳的“天”部分作为散列,并使用完整的时间戳作为范围。

    【讨论】:

    • 由于哈希键需要eq 条件,那么我是否需要对自应用程序上次刷新以来经过的每一天执行查询? (应用程序在本地存储lastRefreshedAt 时间戳)。似乎这比扫描更昂贵。
    • 也许我可以将时间戳的“年月”部分存储为哈希键?与使用“日”部分相比,这将显着减少某人在一年内第一次打开他们的应用程序时所需的查询数量。此时,范围键似乎也变得无关紧要,因为通过哈希键查询会带来自lastRefreshedAt以来已更新的所有项目。
    猜你喜欢
    • 1970-01-01
    • 2016-01-21
    • 1970-01-01
    • 2017-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-09
    • 1970-01-01
    相关资源
    最近更新 更多