Dynamodb

时间:2015-09-16 23:11:07

标签: amazon-web-services amazon-dynamodb nosql

我正在尝试创建一个表来存储DynamoDB中的发票行项目。我们假设该项目由CompanyCodeInvoiceNumberLineItemId,金额和其他订单项详细信息定义。

唯一项目由前3个属性的组合定义。对于不同的项目,这些属性中的任何两个都可以是相同的。我应该选择什么作为哈希属性和范围属性?

3 个答案:

答案 0 :(得分:21)

我相信the first option offered by @georgeaf99不起作用,因为如果你这样做,那么CompanyCode必须在表格中是唯一的。因此,每个公司只允许一个项目。我认为第二种解决方案是唯一真正的方法。

您可以使用CompanyCode作为哈希键,然后结合使项目唯一的所有其他字段(在本例中为InvoiceNumberLineItemId)需要以某种方式合并为一个值(例如与字段分隔符串联),这将是您的范围键。不幸的是,这有点难看,但这就是像DynamoDB这样的NoSQL数据库的本质。但是,它将允许您成功存储具有正确唯一性的记录。回读记录时,如果您不想将组合字段解析回各个部分,则必须为InvoiceNumberLineItemID添加其他单独的字段。

如果每个公司没有大量发票,则只能通过哈希密钥进行查询,并在客户端进行过滤。如果每个公司有大量发票并且只需要查询单个发票的项目,那么我将在CompanyCode和InvoiceNumber上创建二级索引。

答案 1 :(得分:11)

一些介绍

为了提高效率,我会提出完全不同的设计。对于NoSQL数据库(和DynamoDB没什么不同),我们始终需要首先考虑访问模式。另外,如果可能的话,我们应努力将所有数据放入同一张表和多个索引中。从OP和他的评论中可以得出,这是两种访问模式:

  1. 对于公司X,获取完整的发票Y(包括所有项目或项目范围)[基于此comment]
  2. 获取X公司的所有发票[基于此comment]

我们现在想知道什么是好的主键?转换为疑问,什么是好的分区键(PK),什么是好的排序键(SK),我们需要创建哪些二级索引,以及哪种类型的二级索引(本地或全局)?一些提醒:

  • 主键可以位于一列或复合键上
  • 复合主键由分区键和排序键组成
  • 分区键用作确定项目分区的哈希函数的输入
  • 排序键也可以是复合键,这使我们可以在DynamoDB中建立一对多关系的模型,如以下评论链接之一所示:https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-sort-keys.html
  • 在表或索引上创建查询时,您始终需要在分区键上使用'='运算符
  • 在“排序键”上查询范围时,您可以选择KeyConditionExpression,该选项为您提供set of operators for sorting及其之间的所有内容(其中之一是功能begins_with (a, substr)
  • 如果您需要进一步优化查询结果(对投影属性进行过滤),还可以使用FilterExpression
  • 本地二级索引(LSI)与原始表具有相同的分区键,但具有不同的排序键,并根据另一种排序键为您提供了不同的数据视图
  • 全局二级索引(GSI)与原始表具有不同的分区键和排序键,从而使您对数据的看法完全不同
  • 具有相同分区键的所有项目都存储在一起,对于复合主键,按排序键值进行排序。如果集合大小超过10 GB,DynamoDB会按排序键拆分分区。

返回建模

很明显,我们正在处理需要建模并适合同一张表的多个实体。为了满足分区键在表上唯一的条件,CustomerCode是自然的分区键-因此,我将确保其唯一。如果不是,那么您需要问自己:如何为第二种访问模式建模?

假设我们已经在CompanyCode上建立了唯一性,我们可以简化说,它以电子邮件的形式出现(或者可以是域,也可以是代码,但我将使用电子邮件进行演示)。 / p>

  • 公司与发票之间的关系始终为1:很多。
  • 发票和项目之间的关系始终为1:很多。

我建议如下图所示的设计: Proposed design in DynamoDB

  • 如果PK为CompanyCode,SK为InvoiceNumber,则可以存储该公司有关该发票的所有属性。
  • 没有什么可以阻止我添加SK为Customer的记录,这使我可以存储有关公司的所有属性。
  • 使用GSI1,我们将创建反向查找,其中GSI1PK是我的表SK(InvoiceNumber),而我的GSI1SK是我的表PK(CompanyCode)。
  • 我正在使用同一张表存储订单项,其中PK为LineItemId,SK为CompanyCode(仍然是唯一的)
  • 对于项目实体项目,我的GSI1PK仍为InvoiceNumber,我的GSI1SK为LineItemId,这是表PK,因此与“发票”实体项目相同。

现在支持以下访问模式:

  • 如果要获取X公司和所有项目的发票Y(访问方式1):查询表CompanyCode=X,并在排序键上将KeyConditionExpression=运算符一起使用InvoiceNumber。如果要获取与该发票相关的所有项目,我将使用Items投影ProjectionExpression属性。
  • 通过使用先前对公司X和发票Y的查询来检索所有项目,我现在可以在表上运行BatchGetItem API调用(使用我的唯一组合键LineItemId+CompanyCode)以获取属于该项目的所有项目该特定客户的特定发票。 (这带有BatchGetItem API的一些限制)
  • 要支持访问模式2,我将对PK上的CompanyCode=X进行查询,并在SK上将KeyConditionExpression函数/运算符与begins_with (a, substr)一起使用,以仅获取公司X的发票,而不获取公司X的发票有关该公司的元数据。这将为我提供给定公司/客户的所有发票。
  • 此外,在GSI1以上的情况下,对于任何给定的InvoiceNumber,我都可以轻松地选择属于该特定发票的所有行项目。 记住: The key values in a global secondary index do not need to be unique-因此,在我的GSI1中,我可以很容易地开具发票_1->(项目_1,item_2),然后又有另一个发票_1->(项目_1,item_2),但是GSI将位于SK中(它将与不同的CompanyCode相关联(但出于演示目的,我使用了invoice_1和invoice_2)。

答案 2 :(得分:7)

我确定你已经发现你的主键(哈希+范围)不能超过两个属性。因此,根据您要执行的查询类型和数据大小,您可以采用不同的方式构建表格。

(针对您上面提到的查询类型进行了优化:仅CompanyCode&全部3)

适用于中小型数据集的最佳解决方案:

  • 哈希键:CompanyCode
  • 仅使用CompanyCode和执行查询 然后根据其他两个属性过滤结果

大数据集的最佳解决方案:

  • 哈希键:CompanyCode
  • 范围键:InvoiceNumber + LineItemId
  • 这允许您仅查询索引,但表结构非常难看