使用 DynamoDB 作为在线商店的数据存储 - Amazon DynamoDB
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

使用 DynamoDB 作为在线商店的数据存储

此使用案例讨论了使用 DynamoDB 作为在线商店(或电子商店)的数据存储。

应用场景

在线商店可让用户浏览不同的商品并最终购买它们。根据生成的发票,客户可以使用折扣码或礼品卡付款,然后使用信用卡支付剩余金额。采购的商品将从几个仓库中的一个仓库中挑选,并发运到提供的地址。在线商店的典型访问模式包括:

  • 获取给定 customerId 的客户

  • 获取给定 productId 的商品

  • 获取给定 warehouseId 的仓库

  • 通过 productId 获取所有仓库的商品库存

  • 获取给定 orderId 的订单

  • 获取给定 orderId 的所有商品

  • 获取给定 orderId 的发票

  • 获取给定 orderId 的所有货件

  • 获取给定日期范围内给定 productId 的所有订单

  • 获取给定 invoiceId 的发票

  • 获取给定 invoiceId 的所有付款

  • 获取给定 shipmentId 的货件详细信息

  • 获取给定 warehouseId 的货件

  • 获取给定 warehouseId 的所有商品的库存

  • 获取给定日期范围内给定 customerId 的所有发票

  • 获取给定日期范围内给定 customerId 订购的所有商品

实体关系图

这是实体关系图(ERD),我们将使用它来将 DynamoDB 建模为在线商店的数据存储。

此 ERD 表示包含 Product、Order、Payment 和 Customer 等实体的在线商店数据模型。

访问模式

当使用 DynamoDB 作为在线商店的数据存储时,我们将考虑这些访问模式。

  1. getCustomerByCustomerId

  2. getProductByProductId

  3. getWarehouseByWarehouseId

  4. getProductInventoryByProductId

  5. getOrderDetailsByOrderId

  6. getProductByOrderId

  7. getInvoiceByOrderId

  8. getShipmentByOrderId

  9. getOrderByProductIdForDateRange

  10. getInvoiceByInvoiceId

  11. getPaymentByInvoiceId

  12. getShipmentDetailsByShipmentId

  13. getShipmentByWarehouseId

  14. getProductInventoryByWarehouseId

  15. getInvoiceByCustomerIdForDateRange

  16. getProductsByCustomerIdForDateRange

架构设计的演变

使用NoSQL Workbench for DynamoDB,导入 AnOnlineShop_1.json,以创建名为 AnOnlineShop 的新数据模型和名为 OnlineShop 的新表。请注意,我们使用通用名称 PKSK 作为分区键和排序键。这是一种用于将不同类型的实体存储在同一个表中的做法。

步骤 1:解决访问模式 1 (getCustomerByCustomerId)

导入 AnOnlineShop_2.json 以处理访问模式 1(getCustomerByCustomerId)。有些实体与其他实体没有关系,因此我们将对它们使用相同的 PKSK 值。在示例数据中,请注意,键使用前缀 c#,以便将 customerId 与稍后添加的其他实体区分开来。对于其他实体也重复这种做法。

为了解决这种访问模式,GetItem 操作可以与 PK=customerIdSK=customerId 结合使用。

步骤 2:解决访问模式 2 (getProductByProductId)

导入 AnOnlineShop_3.json,以解决 product 实体的访问模式 2(getProductByProductId)。产品实体的前缀为 p#,并且使用了相同的排序键属性来存储 customerID 以及 productID。通用命名和垂直分区允许我们创建这样的项目集合,以实现有效的单表设计。

为了解决这种访问模式,GetItem 操作可以与 PK=productIdSK=productId 结合使用。

步骤 3:解决访问模式 3 (getWarehouseByWarehouseId)

导入 AnOnlineShop_4.json,以解决 warehouse 实体的访问模式 3(getWarehouseByWarehouseId)。我们目前已将 customerproductwarehouse 实体添加到同一个表中。它们使用前缀和 EntityType 属性进行区分。类型属性(或前缀命名)可提高模型的可读性。如果我们只是将不同实体的字母数字 ID 存储在同一属性中,可读性就会受到影响。在没有这些标识符的情况下,很难将一个实体与另一个实体区分开来。

为了解决这种访问模式,GetItem 操作可以与 PK=warehouseIdSK=warehouseId 结合使用。

基表:

使用前缀和 EntityType 按仓库 ID 获取仓库数据的 DynamoDB 表设计。

步骤 4:解决访问模式 4 (getProductInventoryByProductId)

导入 AnOnlineShop_5.json 以解决访问模式 4(getProductInventoryByProductId)。warehouseItem 实体用于跟踪每个仓库中的商品数量。在仓库中添加或移除商品时,通常会更新此项目。从 ERD 中可以看出,productwarehouse 之间存在多对多关系。在此处,从 productwarehouse 的一对多关系建模为 warehouseItem。稍后,也将对从 warehouseproduct 的一对多关系进行建模。

访问模式 4 可以通过查询 PK=ProductIdSK begins_with “w#“ 来解决。

有关 begins_with() 以及其他可应用于排序键的表达式的更多信息,请参阅键条件表达式

基表:

这一表设计用于查询 ProductID 和 warehouseId,来跟踪给定仓库中的产品库存。

步骤 5:解决访问模式 5(getOrderDetailsByOrderId)和 6(getProductByOrderId

通过导入 AnOnlineShop_6.json,向表中添加更多的 customerproductwarehouse 项目。然后,导入 AnOnlineShop_7.json,为 order 构建一个项目集合,该集合可以处理访问模式 5(getOrderDetailsByOrderId)和 6(getProductByOrderId)。您可以看到建模为 orderItem 实体的 orderproduct 之间的一对多关系。

要解决访问模式 5(getOrderDetailsByOrderId),请使用 PK=orderId 查询表。这将提供有关订单的所有信息,包括 customerId 和订购的商品。

基表:

这一表设计使用 orderId 进行查询,来获取有关所有已订购产品的信息。

要解决访问模式 6(getProductByOrderId),我们只需要读取 order 中的商品。使用 PK=orderIdSK begins_with “p#” 查询表来实现这一目标。

基表:

这一表设计使用 orderId 和 productId 进行查询,来获取订单中的产品。

步骤 6:解决访问模式 7(getInvoiceByOrderId

导入 AnOnlineShop_8.json,将 invoice 实体添加到订单 项目集合中,以处理访问模式 7(getInvoiceByOrderId)。为了解决这种访问模式,您可以将查询操作与 PK=orderIdSK begins_with “i#” 结合使用。

基表:

这一表设计在订单项目集合中使用发票实体,来按 orderId 获取发票。

步骤 7:解决访问模式 8(getShipmentByOrderId)

导入 AnOnlineShop_9.json,将 shipment 实体添加到订单 项目集合中,以解决访问模式 8(getShipmentByOrderId)。我们通过在单表设计中添加更多类型的实体来扩展相同的垂直分区模型。请注意订单 项目集合如何包含 order 实体与 shipmentorderIteminvoice 实体之间的不同关系。

要按 orderId 获取货件,可以使用 PK=orderIdSK begins_with “sh#” 执行查询操作。

基表:

这一表设计将货件实体添加到订单项目集合中,来按订单 ID 获取货件。

步骤 8:解决访问模式 9(getOrderByProductIdForDateRange)

我们在上一步中创建了一个订单 项目集合。此访问模式具有新的查找维度(ProductIDDate),这要求您扫描整个表并筛选掉相关记录以获取目标项目。为了解决这种访问模式,我们需要创建全局二级索引(GSI)。导入 AnOnlineShop_10.json 以使用 GSI 创建新的项目集合,从而可以从多个订单 项目集合中检索 orderItem 数据。数据现在具有 GSI1-PKGSI1-SK,它们将分别是 GSI1 的分区键和排序键。

DynamoDB 会自动将包含 GSI 的键属性的项目从表填充到 GSI。无需手动在 GSI 中进行任何其他插入。

要解决访问模式 9,请使用 GSI1-PK=productIdGSI1SK between (date1, date2)GSI1 执行查询。

基表:

使用 GSI 的表设计,用于从多个订单项目集合中获取订单数据。

GSI1:

此 GSI 设计以 ProductID 和 Date 作为分区键和排序键,来按产品 ID 和日期获取订单。

步骤 9:解决访问模式 10(getInvoiceByInvoiceId)和 11(getPaymentByInvoiceId

导入 AnOnlineShop_11.json 以解决访问模式问题 10(getInvoiceByInvoiceId)和 11(getPaymentByInvoiceId),这两者都与 invoice 相关。尽管这些是两种不同的访问模式,但它们是使用相同的键条件实现的。Payments 定义为 invoice 实体上具有映射数据类型的属性。

注意

GSI1-PKGSI1-SK 已重载以存储有关不同实体的信息,因此,可以从同一 GSI 提供多种访问模式。有关 GSI 重载的更多信息,请参阅在 DynamoDB 中重载全局二级索引

要解决访问模式 10 和 11,请使用 GSI1-PK=invoiceIdGSI1-SK=invoiceId 查询 GSI1

GSI1:

此 GSI 设计以 invoiceId 作为分区键和排序键,来通过发票 ID 获取发票和付款。

步骤 10:解决访问模式 12(getShipmentDetailsByShipmentId)和 13(getShipmentByWarehouseId

导入 AnOnlineShop_12.json 以解决访问模式问题 12(getShipmentDetailsByShipmentId)和 13(getShipmentByWarehouseId)。

请注意,shipmentItem 实体添加到基表上的订单 项目集合中,以便能够在单个查询操作中检索有关订单的所有详细信息。

基表:

这一表设计在订单项目集合中使用 shipmentItem 实体,来获取所有订单详细信息。

GSI1 分区和排序键已用于对 shipmentshipmentItem 之间的一对多关系进行建模。要解决访问模式 12(getShipmentDetailsByShipmentId),请使用 GSI1-PK=shipmentIdGSI1-SK=shipmentId 查询 GSI1

GSI1:

此 GSI1 设计以 shipmentId 作为分区键和排序键,来按货件 ID 获取货件详细信息。

我们需要创建另一个 GSI(GSI2),来为访问模式 13(getShipmentByWarehouseId)的 warehouseshipment 之间新的一对多关系建模。要解决此访问模式,请使用 GSI2-PK=warehouseIdGSI2-SK begins_with “sh#” 查询 GSI2

GSI2:

此 GSI2 设计以 warehouseId 和 shipmentId 作为分区键和排序键,以便按仓库获取货件。

步骤 11:解决访问模式 14(getProductInventoryByWarehouseId)、15(getInvoiceByCustomerIdForDateRange)和 16(getProductsByCustomerIdForDateRange

导入 AnOnlineShop_13.json,以添加与下一组访问模式相关的数据。要解决访问模式 14(getProductInventoryByWarehouseId),请使用 GSI2-PK=warehouseIdGSI2-SK begins_with “p#” 查询 GSI2

GSI2:

此 GSI2 设计以 warehouseId 和 productId 作为分区键和排序键,来解决访问模式 14。

要解决访问模式 15(getInvoiceByCustomerIdForDateRange),请使用 GSI2-PK=customerIdGSI2-SK between (i#date1, i#date2) 查询 GSI2

GSI2:

此 GSI2 设计以 customerId 和发票日期范围作为分区键和排序键,来解决访问模式 15。

要解决访问模式 16(getProductsByCustomerIdForDateRange),请使用 GSI2-PK=customerIdGSI2-SK between (p#date1, p#date2) 查询 GSI2

GSI2:

此 GSI2 设计以 customerId 和发票日期范围作为分区键和排序键,来解决访问模式 16。
注意

NoSQL Workbench 中,分面 表示应用程序对 DynamoDB 的不同数据访问模式。分面为您提供了一种查看表中数据子集的方法,而不必查看不符合分面约束的记录。分面是一种可视化数据建模工具,在 DynamoDB 中不作为可用的构造存在,因为它们纯粹是对访问模式建模的帮助。

导入 AnOnlineShop_facets.json,以查看此使用案例的各个分面。

下表总结了所有访问模式以及架构设计如何解决访问模式:

访问模式 基表/GSI/LSI 操作 分区键值 排序键值
getCustomerByCustomerId 基表 GetItem PK=customerId SK=customerId
getProductByProductId 基表 GetItem PK=productId SK=productId
getWarehouseByWarehouseId 基表 GetItem PK=warehouseId SK=warehouseId
getProductInventoryByProductId 基表 Query PK=productId SK begins_with "w#"
getOrderDetailsByOrderId 基表 Query PK=orderId
getProductByOrderId 基表 Query PK=orderId SK begins_with "p#"
getInvoiceByOrderId 基表 Query PK=orderId SK begins_with "i#"
getShipmentByOrderId 基表 Query PK=orderId SK begins_with "sh#"
getOrderByProductIdForDateRange GSI1 查询 PK=productId date1 和 date2 之间的 SK
getInvoiceByInvoiceId GSI1 查询 PK=invoiceId SK=invoiceId
getPaymentByInvoiceId GSI1 查询 PK=invoiceId SK=invoiceId
getShipmentDetailsByShipmentId GSI1 查询 PK=shipmentId SK=shipmentId
getShipmentByWarehouseId GSI2 查询 PK=warehouseId SK begins_with "sh#"
getProductInventoryByWarehouseId GSI2 查询 PK=warehouseId SK begins_with "p#"
getInvoiceByCustomerIdForDateRange GSI2 查询 PK=customerId i#date1 和 i#date2 之间的 SK
getProductsByCustomerIdForDateRange GSI2 查询 PK=customerId p#date1 和 p#date2 之间的 SK

在线商店最终架构

这是最终的架构设计。要以 JSON 文件格式下载此架构设计,请参阅 GitHub 上的 DynamoDB 设计模式

基表

带有属性(例如 EntityName 和 Name)的在线商店基表的最终架构。

GSI1

带有属性(例如 EntityType)的在线商店基表的最终 GSI1 架构。

GSI2

带有属性(例如 EntityType)的在线商店基表的最终 GSI2 架构。

在此架构设计中使用 NoSQL Workbench

若要进一步探索和编辑新项目,您可以将此最终架构导入到 NoSQL Workbench,这是一款为 DynamoDB 提供数据建模、数据可视化和查询开发功能的可视化工具。请按照以下步骤开始使用:

  1. 下载 NoSQL Workbench。有关更多信息,请参阅 下载 NoSQL Workbench for DynamoDB

  2. 下载上面列出的 JSON 架构文件,该文件已经采用 NoSQL Workbench 模型格式。

  3. 将 JSON 架构文件导入到 NoSQL Workbench。有关更多信息,请参阅 导入现有数据模型

  4. 导入到 NOSQL Workbench 后,您便可编辑数据模型。有关更多信息,请参阅 编辑现有数据模型

  5. 要将数据模型可视化、添加样本数据或从 CSV 文件导入样本数据,请使用 NoSQL Workbench 的数据可视化工具功能。