Dynamodb中的3个字段复合主键(唯一项)

HHH*_*HHH 35 amazon-web-services nosql amazon-dynamodb

我正在尝试创建一个表来存储DynamoDB中的发票行项目.比方说,该项目是由定义CompanyCode,InvoiceNumberLineItemId,金额等行项目的详细信息.

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

Joe*_*jr2 26

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

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

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


aze*_*pdx 22

一些介绍

为了提高效率,我建议完全不同的设计.使用NoSQL数据库(和DynamoDB没有区别),我们总是需要首先考虑访问模式.此外,如果可能,我们应该努力将所有数据放在同一个表和多个索引中.根据OP和他的评论,这些是两种访问模式:

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

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

  • 主键可以在一列或复合上
  • 复合主键由分区键和排序键组成
  • 分区键用作散列函数的输入,该散列函数将确定项的分区
  • 排序键也可以是复合键,这允许我们在其中一条评论链接中给出的DynamoDB中的一对多关系建模:https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-sort -keys.html
  • 在表或索引上创建查询时,始终需要在分区键上使用"="运算符
  • 在Sort Key上查询范围时,您可以选择为其KeyConditionExpression提供用于排序的操作符集以及介于两者之间的所有内容(其中一个是函数begins_with (a, substr))
  • FilterExpression如果您需要进一步细化查询结果(对投影属性进行过滤),您也可以使用
  • 本地二级索引(LSI)具有相同的分区键,但排序键与原始表不同,并根据备用排序键为您提供不同的数据视图
  • 全局二级索引(GSI)具有与原始表不同的分区键和不同的排序键,并为您提供完全不同的数据视图
  • 具有相同分区键的所有项目一起存储,对于复合主键,按排序键值排序.如果集合大小超过10 GB,DynamoDB将按分类键拆分分区.

回到建模

很明显,我们正在处理需要建模并适合同一个表的多个实体.为了满足分区键在表上唯一的条件,CompanyCode作为一个自然的分区键 - 所以我将确保它是唯一的.如果没有,那么你需要问问自己如何建模第二种访问模式?

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

  • 公司与发票之间的关系总是1:很多.
  • 发票和物品之间的关系总是1:很多.

我建议设计如下图所示: DynamoDB中提出的设计

  • 使用PK CompanyCode和SK InvoiceNumber可以存储该公司的发票的所有属性.
  • 没有什么能阻止我在SK上添加记录,Customer这使得我可以存储有关公司的所有属性.
  • 使用GSI1,我们将创建反向查找,其中GSI1PK是我的表SK(InvoiceNumber),而我的GSI1SK是我的表PK(CompanyCode).
  • 我正在使用相同的表存储具有PK的行项目LineItemId和SK正在CompanyCode(仍然是唯一的)
  • 对于Item实体项目,我的GSI1PK仍然是InvoiceNumber,我的GSI1SK LineItemId是表PK,因此它与Invoice实体项目相同.

现在支持的访问模式:

  • 如果我想获得发票Y表示X公司和所有的项目(访问模式1):查询表,其中CompanyCode=X并使用KeyConditionExpression=操作上的排序键InvoiceNumber.如果我想获得与该发票绑定的所有项目,我将Items使用项目属性ProjectionExpression.
  • 通过检索公司X和发票Y的先前查询的所有项目,我现在可以在表格上运行BatchGetItemAPI调用(使用我的唯一复合密钥LineItemId+CompanyCode)以获取属于该特定客户的特定发票的所有项目.(这附带了BatchGetItem API的一些约束)
  • 为了支持访问模式2,我将CompanyCode=X使用PK 进行查询并在KeyConditionExpressionSK上使用begins_with (a, substr)函数/运算符来仅获取公司X的发票而不是该公司的元数据.这将为我提供给定公司/客户的所有发票.
  • 此外,对于上述GSI1,对于任何给定的InvoiceNumber我都可以轻松选择属于该特定发票的所有行项目.记住: 全局二级索引中的关键值不需要是唯一的 - 所以在我的GSI1中,我可以轻松开发invoice_1 - >(item_1,item_2),然后另一个invoice_1 - >(item_1,item_2),但两者之间存在差异GSI中的项目将在SK中(它将与不同相关联CompanyCode(但为了演示目的,我使用了invoice_1和invoice_2).


小智 8

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

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

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

  • 散列键: CompanyCode
  • 仅使用查询执行查询CompanyCode,然后在其他两个属性上过滤结果

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

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

  • 或将CompanyCode + InvoiceNumber作为哈希,将LineItemId作为范围,并为CompanyCode添加二级索引。这样,您可以按公司查询,也可以查询特定公司的特定发票。似乎没有必要仅按发票号查询,因为据推测,相同的发票号可以用于多家公司。 (3认同)
  • 对 jarmod 的建议+1。使用 CompanyCode+InvoiceNumber 可以为您的应用程序提供更好的基数和最终的可扩展性。例如,如果 CompanyCode 收到大量新发票的写入,则该哈希键/分区将受到猛击,而不是通过您的表进行分发 (2认同)
  • @jarmod Dynamodb 本身有没有办法处理这种连接,我们可以将这些字段作为单独的字段传递?此外,我想在二级索引中将这些字段作为单独的字段访问,而不将其作为重复项再次存储在其他字段中。 (2认同)
  • @HHH我不相信Dynamo会为您做串联。如果您想单独查询它们,那么我认为您将不得不在哈希键之外存储和索引它们。如果您要向公司开出这么多发票,以致于数据重复会导致问题,那么这将是一个非常好的问题;-) (2认同)