首页 > 解决方案 > 如何在 DynamoDB 中选择日期范围内的项目

问题描述

如何选择给定日期范围内的所有项目?

SELECT * FROM GameScores where createdAt >= start_date && createAt <=end_date

我想进行这样的查询。我是否需要创建全局二级索引?

我试过这个

public void getItemsByDate(Date start, Date end) {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    String stringStart = df.format(start);
    String stringEnd = df.format(end);

    ScanSpec scanSpec = new ScanSpec();
    scanSpec.withFilterExpression("CreatedAt BETWEEN :from AND :to")
            .withValueMap(
                    new ValueMap()
                            .withString(":from", stringStart)
                            .withString(":to", stringEnd));

    ItemCollection<ScanOutcome> items = null;
    items = gamesScoresTable.scan(scanSpec);
}

但它不起作用,我得到的结果比预期的要少。

标签: javaamazon-web-servicesamazon-dynamodb

解决方案


我可以回答您的问题,但要提出任何真正的解决方案,我需要查看您的数据的一般形式,以及您的 GameScore 的主键是什么。

TLDR;

设置您的表,以便您可以使用查询而不是扫描和过滤器来检索数据,然后创建索引以支持较少使用的访问模式并提高查询灵活性。由于在提供完整(或者,虽然没有那么快,部分)主键(即使用查询)时读取速度非常快,因此当表结构由应用程序的访问模式驱动时,DynamoDB 是最佳选择。

在设计表时,请牢记NoSQL 设计最佳实践,以及查询和扫描的最佳实践,从长远来看,它会带来好处。

解释

问题 1

如何选择给定日期范围内的所有项目?

为了回答这个问题,我想进一步分解这个问题。让我们开始:如何选择所有项目?

这一点,你已经完成了。扫描是检索表中所有项目的好方法,除非您将所有项目都放在一个分区中,否则它是检索表中所有项目的唯一方法。当您必须通过未知键访问数据时,扫描会很有帮助。

然而,扫描有局限性,并且随着您的表格大小的增加,它们会在性能和成本方面花费您。单次扫描最多只能检索单个分区的 1MB 数据,并且以该分区的读取容量为上限。当扫描达到任一限制时,连续扫描将按顺序进行。这意味着对一张大桌子的扫描可能需要多次往返。

最重要的是,通过扫描,无论返回多少(或很少)数据,您都会根据项目的大小消耗读取容量。如果您只在 ProjectionExpression 中请求少量属性,而您的 FilterExpression 消除了表格中 90% 的项目,您仍然需要付费阅读整个表格。

您可以使用Parallel Scans优化扫描性能,但如果您需要对应用程序经常发生的访问模式进行整个表扫描,则应考虑重组表。更多关于扫描

现在让我们看看:如何根据某些标准选择所有项目?

根据某些条件(在您的情况下SELECT * FROM GameScores where createdAt >= start_date && createAt <=end_date)完成检索数据的理想方法是查询基表(或索引)。为此,根据文档:

您必须提供分区键属性的名称和该属性的单个值。查询返回具有该分区键值的所有项目。

就像文档说的那样,查询一个分区将返回它的所有值。如果您的 GameScores 表具有 GameName 的分区键,那么对 GameName = PacMan 的查询将返回具有该分区键的所有项目。但是,此查询中不会捕获其他 GameName 分区。

如果您需要更深入的查询:

或者,您可以提供排序键属性并使用比较运算符来优化搜索结果。

以下是您可以与排序键一起使用的所有可能的比较运算符的列表。这是您可以between在查询操作的 KeyConditionExpression 中利用比较运算符的地方。如果 createdAt 是您正在查询的表或索引的排序键,则类似:GameName = PacMan AND createdAt BETWEEN time1 AND time2将起作用。

如果它不是排序键,您可能会得到第二个问题的答案。

问题2

我需要创建全球二级索引吗?

让我们开始:我需要创建索引吗?

如果您的基表数据结构不适合您的应用程序的某些访问模式,您可能需要这样做。但是,在 DynamoDB 中,数据的非规范化也支持更多的访问模式。我建议您观看有关如何构建数据的视频。

继续:我需要创建 GSI 吗?

GSI 不支持强读取一致性,因此如果需要,您需要使用本地二级索引 (LSI)。但是,如果您已经创建了基表,则无法创建 LSI。两者之间的另一个区别是主键:GSI 可以有不同的分区和排序键作为基表,而 LSI 只能在排序键上有所不同。更多关于索引


推荐阅读