【问题标题】:Guidelines for creating GSI in DynamoDB在 DynamoDB 中创建 GSI 的指南
【发布时间】:2016-11-30 18:40:47
【问题描述】:

想象一下,您需要持久化可以用以下模式表示的东西:

{
  type: String
  createdDate: String (ISO-8601 date)
  userId: Number
  data: {
    reference: Number,
    ...
  }
}

typecreatedDate 始终是定义/必需的,其他所有内容,例如 userIddata 以及 data 中的任何字段都是可选的。 typecreatedDate 的组合不保证任何唯一性。数据中的字段数(当data 存在时)可能不同。

现在假设您需要针对以下结构进行查询:

  1. 给我type 等于某物的项目
  2. 给我userId 等于某物的项目
  3. 给我type AND userId 等于某物的项目
  4. 给我userId AND data.reference 等于某物的项目
  5. 给我一些userId 等于某物、type 在值范围内以及data.reference 等于某物的项目

在我看来,需要在表级别引入 HashKey 以唯一匹配项目。我唯一的选择是使用 UUID 生成器之类的东西。基于此,我无法从上述我需要的表中查询任何内容。 所以我需要创建几个全局二级索引来覆盖上述所有第五种情况,如下所示:

  1. 对于第一个用例,我可以创建 GSI,其中type 可以是 HashKey,createdDate 可以是 RangeKey。正如我所提到的,从这里开始困扰我的是,这个复合键很有可能不是唯一的。
  2. 对于第二个用例,我可以创建 GSI,其中userId 可以是 HashKey,createdDate 可以是 RangeKey 这里可能这个复合键可以唯一地匹配项目。
  3. 对于第三个用例,我可能有两种解决方案。要么创建第三个 GSI,其中type 可以是 HashKey,userId 可以是 RangeKey。使用这种方法,我失去了对返回的数据进行排序的能力,并且同样担心,这个复合键不能保证唯一性。另一种方法是使用之前的两个 GSI 之一并使用 FilterExpression,对吗?
  4. 对于第四个用例,我只有一个选项。使用以前的 GSI,userId 作为 HashKey,createdDate 作为 RangeKey,并对 data.reference 使用 FilterExpression。无法在嵌套对象的字段上创建索引对吗?
  5. 对于第五个用例,因为 IN 运算符仅通过 FilterExpression 支持(对吗?),唯一的选择是使用 GSI,userId 作为 HashKey,createdDate 作为 RangeKey,并使用typedata.reference 的 FilterExpression?

因此,作为这个问题的唯一亮点,我看到使用 GSI,userId 作为 HashKey,createdDate 作为 RangeKey。 但同样,userId 不是必填字段,它可以为 NULL。 HashKey 不能为 NULL 对吧?

最重要的是,如果复合键(HashKey 和 RangeKey)不能保证唯一性,这意味着使用索引中已经存在的复合键保存项目将默默地重写前一个项目,这意味着我将丢失数据?

【问题讨论】:

  • 您的数据更新频率如何?
  • @HarshalBulsara 经常
  • 我觉得你可以看看 CloudSearch 服务,它可能会有所帮助。
  • 您能否详细说明一下,CloudSearch 是什么?为什么不使用常规 API 与 DynamoDB 交互?
  • 云搜索是另一个 AWS 服务,它可以与 DynamoDB 集成用于搜索目的,因为您想查询您的非关键属性,再加上许多 GSI 没有任何意义

标签: amazon-web-services amazon-dynamodb


【解决方案1】:

关于 DynamoDB:它是一个非 SQL 数据库。从好的方面来说,只要你有一个唯一的索引,就可以很容易地将几乎任何东西转储到其中,并且如果你有一个很好的分区键可以将你的数据细分为块,那么它将被相当有效地存储以供检索。不利的一面是,根据定义,您对不是分区键或索引(主或辅助)的字段执行的任何查询都是慢表扫描。 DynamoDB 不是 SQL 数据库,在过滤非索引列时无法提供类似 SQL 的性能。如果您看到的性能是合理的,您需要在执行查询之前将查询结果分隔为可用的预先计算的索引值,或者您需要知道您要查找的结果被分隔为几个分区键。

首先让我们考虑分隔的分区键路由。一旦您尽可能多地分隔了分区键并且没有更多索引可供参考,您询问 DynamoDB 的其他所有内容都不是真正的查询,而是表扫描。您可以要求 DynamoDB 为您完成此操作,但您最好从分区键或索引查询中获取完整结果,并使用您使用的任何语言自己进行过滤。我为此使用 Java,因为通过 Java->DynamoDB API 查询我需要的键很简单,然后很容易在 Java 中过滤结果。如果您对此感兴趣,我可以整理一些简单的示例。

如果你走索引和过滤路线,了解哈希键仍然是索引的分区键,这将决定 GSI 可以并行使用多少。您的 DynamoDB 表越大,您的查询对时间越敏感,这将成为更大的问题。

所以是的,您可以使用索引进行您想要的查询,尽管需要对这些索引进行一些仔细的规划。

1. For first use case i could create GSI where type can be HashKey and
createdDate can be RangeKey.What bothers me from start here as i
mentioned, there is high chance for this composite key to NOT be
unique. 

GSI 不必是唯一的。您将在查询中收到多行,但从 DynamoDB 的角度来看,不会有任何问题。但是,如果您使用 type 作为分区键 (HashKey),则此查询的性能可能会很差,除非您的每个 type 值的记录很少。

2. For second use case i could crate GSI where userId can be HashKey and
createdDate can be RangeKey Here probably this composite key can match item
uniquely. 

只要您的 userId 在某一天是唯一的,这里就没有问题。

3. For third use case, i have probably two solutions. Either to create third
GSI where type can be HashKey and userId can be RangeKey. With that approach
i'm losing ability to sort returned data and again same worries, this
composite key does not guarantee uniqueness. Another approach would be to 
use one of two previous GSIs and using FilterExpression, right?

所以 RangeKey 是您的排序键,至少从 DynamoDB 的角度来看是这样。是的,如果您使用 GSI,然后使用过滤器,那么您就是在对 GSI 索引行的内容进行表扫描。但是,是的,如果您正在组合两个 GSI,您要么提前生成第三个索引,要么过滤/扫描。 DynamoDB 没有对两个索引执行 INNER JOIN 的规定。并且将 type 作为分区键然后过滤结果会导致严重的性能问题。

4. For fourth use case i have only one option. To use previous GSI with
userId as HashKey and createdDate as a RangeKey and to use FilterExpression
against data.reference. Index can't be created on fields from nested object
right?

我不确定您的嵌套对象问题,但是可以,使用带有过滤器/扫描的先前 GSI 会起作用。

5. For fifth use case, because IN operator is only supported via
FilterExpression (right?) only option again is to use GSI with userId as
HashKey and createdDate as a RangeKey and to use FilterExpression for both
type and data.reference?

是的,如果您希望 DynamoDB 为您完成这项工作,这是处理您的第五个查询的方法。但我回到我原来的陈述:为什么要这样做?如果您可以创建一个 GSI 来有效地获取您感兴趣的记录,请使用 GSI。但是当我从不使用过滤器表达式时:我会从查询中获取完整的分区、索引或 GSI 结果,并使用我选择的编程语言自己进行过滤。

如果您需要在 DynamoDB 中执行所有操作,您的方法将起作用,但它们可能不会很快,具体取决于过滤的行数。我非常努力地解决了性能问题,因为我看到很多工作都投入到 s 数据库项目中,然后由于性能不佳使其无法使用,所以整个事情都没有得到使用。

【讨论】:

    猜你喜欢
    • 2015-04-08
    • 1970-01-01
    • 2018-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多