在不使用分区键的情况下查询DynamoDB表的全局二级索引。

12 浏览
0 Comments

在不使用分区键的情况下查询DynamoDB表的全局二级索引。

我的DynamoDB表中的分区键是userID,没有排序键。表中每个项目还有一个timestamp属性。我想要检索所有在指定范围内的具有时间戳的项目(不考虑userID,即跨越所有分区的范围)。\n在阅读文档和搜索Stack Overflow(这里)之后,我发现我需要为我的表创建一个GSI。\n因此,我创建了一个具有以下键的GSI:\n

    \n

  • 分区键:userID
  • \n

  • 排序键:timestamp
  • \n

\n我正在使用以下代码使用Java SDK查询索引:\n

String lastWeekDateString = getLastWeekDateString();
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("user table");
Index index = table.getIndex("userID-timestamp-index");
QuerySpec querySpec = new QuerySpec()
    .withKeyConditionExpression("timestamp > :v_timestampLowerBound")
    .withValueMap(new ValueMap()
            .withString(":v_timestampLowerBound", lastWeekDateString));
    ItemCollection items = index.query(querySpec);
    Iterator iter = items.iterator();
while (iter.hasNext()) {
    Item item = iter.next();
    // 在此处提取项目属性
}

\n执行此代码时,我遇到了以下错误:\n

查询条件缺少键模式元素:userID

\n根据我所知,我应该能够只使用排序键查询GSI,而不需要在分区键上给出任何条件。请帮我理解我的实现有什么问题。谢谢。\n编辑:在阅读这里的主题后,结果显示我们无法仅通过排序键对GSI进行查询。那么,如果有的话,如何通过属性的范围查询整个表的替代方法?我在那个主题中找到的一个建议是使用年份作为分区键。如果所需范围跨越多年,这将需要多个查询。此外,这不会均匀地将数据分布在所有分区中,因为只有与当前年份对应的分区将用于整整一年的插入。请提供任何替代方案的建议。

0
0 Comments

问题的出现原因:在使用DynamoDB的全局二级索引(GSI)查询时,如果不使用分区键,会导致查询效率低下,需要进行过滤或扫描操作。

解决方法:通过正确设计主键方案,可以解决自定义查询需求。在设计DynamoDB的主键时,主要原则是哈希键(hash key)用于对整个项目进行分区,排序键(sort key)用于对分区内的项目进行排序。推荐将时间戳的年份作为哈希键,月份和日期作为排序键。在这种情况下,最多只需要进行2次查询。

示例代码如下:

如果开始日期和结束日期的年份相同,只需要进行一次查询:

.withKeyConditionExpression("#year = :year and #month-date > :start-month-date and #month-date < :end-month-date")

如果开始日期和结束日期的年份不同,需要进行两次查询:

.withKeyConditionExpression("#year = :start-year and #month-date > :start-month-date")

.withKeyConditionExpression("#year = :end-year and #month-date < :end-month-date")

最后,将两次查询的结果集进行合并。这样最多只消耗2个读取容量单位。为了更好地比较排序键,可能需要使用UNIX时间戳。

感谢阅读!

0
0 Comments

在使用DynamoDB的查询操作时,必须至少指定分区键。这就是为什么会出现"userId"是必需的错误。(在AWS Query文档中)

唯一的方法来获取没有分区键的项是执行扫描操作(但这不会按照排序键进行排序!)。

如果您想获取所有已排序的项,您需要创建一个具有分区键的全局二级索引(GSI),该分区键对于您需要的所有项都是相同的(例如,在所有项上创建一个新属性,如"type": "item")。然后,您可以查询GSI并指定#type=:item。

代码示例:

QuerySpec querySpec = new QuerySpec()
    .withKeyConditionExpression(":type = #item AND timestamp > :v_timestampLowerBound")
    .withKeyMap(new KeyMap()
            .withString("#type", "type"))
    .withValueMap(new ValueMap()
            .withString(":v_timestampLowerBound", lastWeekDateString)
            .withString(":item", "item"));

将"type"作为分区键,并为所有项中的"type"设置相同的值的问题在于读取负载集中在一个分区上,这破坏了DynamoDB的可扩展性。我不想执行扫描操作,因为这会读取所有项并增加很多成本。

0