在DynamoDB中创建GSI的准则

时间:2016-07-26 23:15:30

标签: amazon-web-services amazon-dynamodb

想象一下,您需要保留可以使用以下模式表示的内容:

{
  type: String
  createdDate: String (ISO-8601 date)
  userId: Number
  data: {
    reference: Number,
    ...
  }
}
始终定义/要求

typecreatedDate,其他所有内容(例如userIddatadata中的任何字段都是可选的。 typecreatedDate的组合并不保证任何唯一性。数据中的字段数(存在data时)可能不同。

现在假设您需要查询此结构,如:

  1. 给我type等于某事
  2. 的项目
  3. 给我userId等于某事
  4. 的项目
  5. 向我提供typeuserId等于
  6. 的项目
  7. 向我提供userIddata.reference等于
  8. 的项目
  9. 向我提供userId等于某事的项目,其中type是值范围,data.reference等于某事
  10. 在我看来,需要在表级别引入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. 的字段上创建索引
    6. 对于第五个用例,因为只有通过FilterExpression支持IN运算符(右?),所以再次使用GSI,userId为HashKey,createdDate为RangeKey并对typedata.reference
    7. 使用FilterExpression

      因此,我只看到使用G {userId作为HashKey而createdDate作为RangeKey的问题。 但是userId不是必填字段,它可以是NULL。 HashKey不能为NULL吗?

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

1 个答案:

答案 0 :(得分:0)

关于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的规定。将类型作为分区键然后过滤结果会产生严重的性能问题。

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中执行所有操作,您的方法将起作用,但它们可能不会非常快,具体取决于要过滤的行数。我非常努力地解决了性能问题,因为我已经看到很多工作进入了数据库项目,然后整个事情都没有被使用,因为糟糕的性能使它无法使用。