在 DynamoDB 中打造關聯式資料模型的範例 - Amazon DynamoDB

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

在 DynamoDB 中打造關聯式資料模型的範例

此範例說明如何在 Amazon DynamoDB 中打造關聯式資料模型。DynamoDB 資料表設計對應至顯示在 中的關聯式順序項目結構描述關聯式模型。此設計使用多個專用資料表,而不是單一相鄰清單,提供清晰的操作界限,同時利用策略 GSIs 有效地為所有存取模式提供服務。

設計方法使用彙總導向原則,根據存取模式而非剛性實體邊界來分組資料。關鍵設計決策包括針對具有低存取關聯性的實體使用個別資料表、一律同時存取時內嵌相關資料,以及使用項目集合來識別關係。

下表及其隨附的索引支援關聯式順序項目結構描述:

員工資料表設計

員工資料表會將員工資訊儲存為每個項目的單一實體,針對直接員工查詢進行最佳化,並透過策略 GSIs 支援多個查詢模式。此資料表示範為具有獨立操作特性和低跨實體存取相關性的實體設計個別資料表的原則。

資料表使用不含排序索引鍵的簡單分割區索引鍵 (employee_id),因為每個員工都是不同的實體。四個 GSIs 可讓不同屬性有效率地查詢:

  • EmployeeByName GSI - 將 INCLUDE 投影與所有員工屬性搭配使用,以支援依名稱的完整員工詳細資訊擷取,並以 employee_id 作為排序索引鍵處理潛在的重複名稱

  • EmployeeByWarehouse GSI - 僅將 INCLUDE 投影與基本屬性 (name, job_title, hire_date) 搭配使用,將儲存成本降至最低,同時支援以倉儲為基礎的查詢

  • EmployeeByJobTitle GSI - 使用 INCLUDE 投影啟用角色型查詢以進行報告和組織分析

  • EmployeeByHireDate GSI - 使用靜態分割區索引鍵值 "EMPLOYEE" 搭配 hire_date 作為排序索引鍵,以啟用最近招聘的高效日期範圍查詢。由於員工新增/更新通常低於 1,000 WCU,因此單一分割區可以處理寫入負載,而不會發生熱分割區問題

員工資料表 - 基礎資料表結構
employee_id (PK) name phone_numbers 倉儲 ID job_title hire_date entity_type
emp_001 John Smith 【"+1-555-0101"】 wh_sea 管理員 2024-03-15 員工
emp_002 Jane Doe 【"+1-555-0102"、"+1-555-0103"】 wh_sea 關聯 2025-01-10 員工
emp_003 Bob Wilson 【"+1-555-0104"】 wh_pdx 關聯 2025-06-20 員工
emp_004 Alice 棕色 【"+1-555-0105"】 wh_pdx 主管 2023-11-05 員工
emp_005 Charlie Davis 【"+1-555-0106"】 wh_sea 關聯 2025-12-01 員工
EmployeeByName GSI - 支援員工名稱查詢
名稱 (GSI-PK) employee_id (GSI-SK) phone_numbers 倉儲 ID job_title hire_date
Alice 棕色 emp_004 【"+1-555-0105"】 wh_pdx 主管 2023-11-05
Bob Wilson emp_003 【"+1-555-0104"】 wh_pdx 關聯 2025-06-20
Charlie Davis emp_005 【"+1-555-0106"】 wh_sea 關聯 2025-12-01
Jane Doe emp_002 【"+1-555-0102"、"+1-555-0103"】 wh_sea 關聯 2025-01-10
John Smith emp_001 【"+1-555-0101"】 wh_sea 管理員 2024-03-15
EmployeeByWarehouse GSI - 支援倉儲查詢
warehouse_id (GSI-PK) employee_id (GSI-SK) name job_title hire_date
wh_pdx emp_003 Bob Wilson 關聯 2025-06-20
wh_pdx emp_004 Alice 棕色 主管 2023-11-05
wh_sea emp_001 John Smith 管理員 2024-03-15
wh_sea emp_002 Jane Doe 關聯 2025-01-10
wh_sea emp_005 Charlie Davis 關聯 2025-12-01
EmployeeByJobTitle GSI - 支援任務標題查詢
job_title (GSI-PK) employee_id (GSI-SK) name 倉儲 ID hire_date
關聯 emp_002 Jane Doe wh_sea 2025-01-10
關聯 emp_003 Bob Wilson wh_pdx 2025-06-20
關聯 emp_005 Charlie Davis wh_sea 2025-12-01
管理員 emp_001 John Smith wh_sea 2024-03-15
主管 emp_004 Alice 棕色 wh_pdx 2023-11-05
EmployeeByHireDate GSI - 支援最近僱用查詢
entity_type (GSI-PK) hire_date (GSI-SK) employee_id name 倉儲 ID
員工 2023-11-05 emp_004 Alice 棕色 wh_pdx
員工 2024-03-15 emp_001 John Smith wh_sea
員工 2025-01-10 emp_002 Jane Doe wh_sea
員工 2025-06-20 emp_003 Bob Wilson wh_pdx
員工 2025-12-01 emp_005 Charlie Davis wh_sea

客戶資料表設計

客戶資料表使用 account_rep_id 的策略非正規化來維護客戶資訊,以啟用有效的帳戶代表查詢。此設計選擇會降低查詢效能的儲存體額外負荷,因此不需要在客戶和帳戶代表資料之間加入。

資料表支援每位客戶使用清單屬性的多個電話號碼,示範 DynamoDB 的結構描述彈性。單一 GSI 可啟用帳戶代表性工作流程:

  • CustomerByAccountRep GSI - 使用 INCLUDE 投影搭配名稱和電子郵件屬性,以支援帳戶代表客戶管理,而不需要完整的客戶記錄擷取

客戶資料表 - 基礎資料表結構
customer_id (PK) name phone_numbers email account_rep_id
cust_001 Acme Corp 【"+1-555-1001"】 contact@acme.com rep_001
cust_002 TechStart Inc 【"+1-555-1002"、"+1-555-1003"】 info@techstart.com rep_001
cust_003 全球交易員 【"+1-555-1004"】 sales@globaltraders.com rep_002
cust_004 BuildRight LLC 【"+1-555-1005"】 orders@buildright.com rep_002
cust_005 FastShip Co 【"+1-555-1006"】 support@fastship.com rep_003
CustomerByAccountRep GSI - 支援帳戶代表查詢
account_rep_id (GSI-PK) customer_id (GSI-SK) name email
rep_001 cust_001 Acme Corp contact@acme.com
rep_001 cust_002 TechStart Inc info@techstart.com
rep_002 cust_003 全球交易員 sales@globaltraders.com
rep_002 cust_004 BuildRight LLC orders@buildright.com
rep_003 cust_005 FastShip Co support@fastship.com

訂單資料表設計

訂單資料表針對訂單標頭和訂單項目使用具有個別項目的垂直分割。此設計可實現高效的產品型查詢,同時在同一分割區中維護所有訂單元件,以實現高效存取。每個訂單包含多個項目:

  • 訂單標頭 - 包含具有 PK=order_id、SK=order_id 的訂單中繼資料

  • 訂單項目 - PK=order_id、SK=product_id 的個別明細項目,啟用直接產品查詢

注意

這種垂直分割方法會交換內嵌訂單項目的簡單性,以增強查詢彈性。每個訂單項目都會成為單獨的 DynamoDB 項目,可實現高效的產品型查詢,同時將所有訂單資料維持在同一個分割區中,以便在單一請求中高效擷取。

資料表包含 account_rep_id (與客戶資料表重複) 的策略非正規化,以啟用直接帳戶代表查詢,而不需要客戶查詢。對於高輸送量寫入案例,OPEN 訂單包含狀態和碎片屬性,以啟用跨多個分割區的寫入碎片。

四個 GSIs支援具有最佳化投影的不同查詢模式:

  • OrderByCustomerDate GSI - 使用 INCLUDE 投影搭配訂單摘要和項目詳細資訊,以支援具有日期範圍篩選的客戶訂單歷史記錄

  • OpenOrdersByDate GSI (稀疏、碎片) - 使用具有 5 個碎片的多屬性分割區索引鍵 (狀態 + 碎片),以跨分割區分配 5,000 WPS (每秒寫入數) (每個 WPS,每個分割區都符合 DynamoDB 的 1,000 WCU)。僅索引 OPEN 訂單 (總計 20%),這有助於降低 GSI 儲存成本。需要跨所有 5 個碎片與用戶端結果合併的平行查詢

  • OrderByAccountRep GSI - 使用 INCLUDE 投影搭配訂單摘要屬性,以支援沒有完整訂單詳細資訊的帳戶代表工作流程

  • ProductInOrders GSI - 從 OrderItem 記錄 (PK=order_id、SK=product_id) 建立,此 GSI 可讓查詢尋找包含特定產品的所有訂單。使用 INCLUDE 投影搭配訂單內容 (customer_id、order_date、quity) 進行產品需求分析

順序資料表 - 基礎資料表結構 (垂直分割)
PK SK customer_id order_date status account_rep_id 數量 價格 碎片
ord_001 ord_001 cust_001 2025-11-15 CLOSED rep_001
ord_001 prod_100 5 25.00
ord_002 ord_002 cust_001 2025-12-20 OPEN rep_001 0
ord_002 prod_101 10 15.00
ord_003 ord_003 cust_002 2026-01-05 OPEN rep_001 2
ord_003 prod_100 3 25.00
OrderByCustomerDate GSI - 支援客戶訂單查詢
customer_id (GSI-PK) order_date (GSI-SK) order_id status total_amount order_items 碎片
cust_001 2025-11-15 ord_001 CLOSED 225.00 {product_id: "prod_100",數量:5}】
cust_001 2025-12-20 ord_002 OPEN 150.00 {product_id: "prod_101",數量:10}】 0
cust_002 2026-01-05 ord_003 OPEN 175.00 {product_id: "prod_100",數量:3}】 2
cust_003 2025-10-10 ord_004 CLOSED 250.00 {product_id: "prod_101",數量:5}】
cust_004 2026-01-03 ord_005 OPEN 200.00 {product_id: "prod_100",數量:20}】 1
OpenOrdersByDate GSI (稀疏、碎片) - 支援高輸送量的開放訂單查詢
狀態 (GSI-PK-1) 碎片 (GSI-PK-2) order_date (SK) order_id customer_id account_rep_id order_items total_amount
OPEN 0 2025-12-20 ord_002 cust_001 rep_001 {product_id: "prod_101",數量:10}】 150.00
OPEN 1 2026-01-03 ord_005 cust_004 rep_002 {product_id: "prod_100",數量:20}】 200.00
OPEN 2 2026-01-05 ord_003 cust_002 rep_001 {product_id: "prod_100",數量:3}】 175.00
OrderByAccountRep GSI - 支援帳戶代表訂單查詢
account_rep_id (GSI-PK) order_date (GSI-SK) order_id customer_id status total_amount
rep_001 2025-11-15 ord_001 cust_001 CLOSED 225.00
rep_001 2025-12-20 ord_002 cust_001 OPEN 150.00
rep_001 2026-01-05 ord_003 cust_002 OPEN 175.00
rep_002 2025-10-10 ord_004 cust_003 CLOSED 250.00
rep_002 2026-01-03 ord_005 cust_004 OPEN 200.00
ProductInOrders GSI - 支援產品訂單查詢
product_id (GSI-PK) order_id (GSI-SK) customer_id order_date 數量
prod_100 ord_001 cust_001 2025-11-15 5
prod_100 ord_003 cust_002 2026-01-05 3
prod_101 ord_002 cust_001 2025-12-20 10

產品資料表設計

產品資料表使用項目集合模式,將產品中繼資料和庫存資料存放在相同的分割區中。此設計利用產品和庫存之間的識別關係 - 庫存不能在沒有父產品的情況下存在。使用 PK=product_id 搭配 SK=product_id 用於產品中繼資料和 SK=warehouse_id 用於清查項目,不需要單獨的清查資料表和 GSI,將成本降低約 50%。

此模式可有效查詢個別倉儲庫存 (GetItem 搭配複合索引鍵) 和產品的所有倉儲庫存 (分割區索引鍵上的查詢)。產品中繼資料項目中的 total_inventory 屬性提供非標準化彙總,以快速查詢總庫存。

產品資料表 - 基礎資料表結構 (項目集合模式)
product_id (PK) warehouse_id (SK) product_name category unit_price inventory_quantity total_inventory
prod_100 prod_100 小工具 A 硬體 25.00 500
prod_100 wh_sea 200
prod_100 wh_pdx 150
prod_100 wh_atl 150
prod_101 prod_101 小工具 B 電子 50.00 300
prod_101 wh_sea 100
prod_101 wh_pdx 200

每個資料表都設計有特定的全域次要索引 (GSIs),可有效支援所需的存取模式。此設計使用彙總導向原則搭配策略去標準化和稀疏索引,以最佳化效能和成本。

主要設計最佳化包括:

  • Sparse GSI - OpenOrdersByDate 僅索引 OPEN 訂單 (總計 20%),這有助於降低 GSI 儲存成本

  • 項目收集模式 - 產品資料表使用 PK=product_id、SK=warehouse_id 存放庫存,以消除單獨的庫存資料表

  • Order + OrderItems 彙總 - 由於 100% 存取關聯而內嵌為單一項目

  • 策略非規範化 - 訂單表格中的 account_rep_id 重複,用於有效率的查詢

最後,您可以再次瀏覽先前定義的存取模式。下表顯示如何使用具有策略 GSIs多資料表設計,有效率地支援每個存取模式。每個模式都使用直接金鑰查詢或單一 GSI 查詢,避免昂貴的掃描,並在任何規模提供一致的效能。

序號 存取模式 查詢條件

1

按員工 ID 查詢員工詳細資訊

員工資料表:GetItem(employee_id="emp_001")

2

按員工姓名查詢員工詳細資訊

EmployeeByName GSI:Query(name="John Smith")

3

尋找員工的電話號碼 (s)

員工資料表:GetItem(employee_id="emp_001")

4

尋找客戶的電話號碼 (s)

客戶資料表:GetItem(customer_id="cust_001")

5

在日期範圍內為客戶取得訂單

OrderByCustomerDate GSI: Query(customer_id="cust_001", order_date BETWEEN "2025-01-01" AND "2025-12-31")

6

顯示日期範圍內的所有開啟的訂單

OpenOrdersByDate GSI:平行查詢 5 個碎片與多屬性 PK (status="OPEN" + shard=0-4)、SK=order_date BETWEEN "2025-01-01" AND "2025-12-31"、合併結果

7

查看所有最近僱用的員工

EmployeeByHireDate GSI: Query(entity_type="EMPLOYEE", hire_date >= "2025-01-01")

8

尋找 Warehouse 中的所有員工

EmployeeByWarehouse GSI:Query(warehouse_id="wh_sea")

9

取得產品訂單上的所有項目

ProductInOrders GSI:Query(product_id="prod_100")

10

在所有倉儲取得產品的庫存

產品資料表:Query(product_id="prod_100")

11

依帳戶代表取得客戶

CustomerByAccountRep GSI:Query(account_rep_id="rep_001")

12

依帳戶代表取得訂單

OrderByAccountRep GSI:Query(account_rep_id="rep_001")

13

取得具有任務標題的員工

EmployeeByJobTitle GSI: Query(job_title="Manager")

14

依產品和倉儲取得庫存

產品資料表:GetItem(product_id="prod_100", warehouse_id="wh_sea")

15

取得產品庫存總計

產品資料表:GetItem(product_id="prod_100", warehouse_id="prod_100")