DynamoDB 的單一表格設計
從 SQL 過來的直覺是每個實體一張表:customers、orders、order_items。在 DynamoDB 中那個直覺通常是錯的。一張儲存每一種實體、以重載的鍵前綴來區分的單一表格,能讓你在一次 Query 中取得一個父項與它的所有子項 — 沒有 join,沒有 N+1。
什麼是 DynamoDB 的單一表格設計?
單一表格設計把每一種實體 — customer、order、order item — 都存進一張 DynamoDB 表格中,以重載的與排序索引鍵前綴來區分。因為鍵是圍繞你的存取模式而非你的實體來設計,一個父項與它的所有子項就住在同一個中,並在單一個 Query 中一起回傳 — 沒有 join,沒有 N+1 讀取。
核心概念
挑選通用的鍵名稱(PK、SK),並把實體型別編碼進值裡:
| PK | SK | attributes |
|---|---|---|
| CUSTOMER#42 | PROFILE | name, email, plan |
| CUSTOMER#42 | ORDER#2026-001 | total, status |
| CUSTOMER#42 | ORDER#2026-002 | total, status |
現在一個 Query PK = "CUSTOMER#42" 會在單一個計費讀取中回傳個人資料以及每一筆訂單。SK begins_with "ORDER#" 則把範圍縮小到只有訂單。
視覺上,重載的項目會堆疊在單一個之下,成為單一個:
一次讀取這個分割區,就把該顧客與每一筆訂單一起交還給你。
重載的 GSI
同樣的手法也適用於索引。在項目上放一個通用的 GSI1PK/GSI1SK,單一個 就能依每個項目寫進那些屬性的內容服務多種存取模式:
| PK | SK | GSI1PK | GSI1SK |
|---|---|---|---|
| ORDER#001 | METADATA | STATUS#OPEN | 2026-01-04 |
| ORDER#002 | METADATA | STATUS#OPEN | 2026-01-05 |
現在 Query GSI1 WHERE GSI1PK = "STATUS#OPEN" 會依日期列出未結訂單 — 這是基礎表格無法回答的模式。另一種實體可以用它自己的含意重用 GSI1(例如 CATEGORY#books)。一個索引,多種查詢。
多對多:鄰接串列
對於各種關係(一個使用者屬於多個團隊,一個團隊有多個使用者),把這條邊寫兩次、把 id 對調:PK=USER#1, SK=TEAM#9 以及 PK=TEAM#9, SK=USER#1。查詢任一側都會列出另一側 — 這是 DynamoDB 對 join 表的替代做法。
何時不該用單一表格
它並非免費。一張重載的表格更難推敲、更難演進,而且對分析不友善。如果你的存取模式真的未知或不斷改變,或資料主要是分析性的,那麼分開的表格(或另一種儲存)可能才是比較明智的選擇。單一表格在模式已知且高流量時才會勝出。
錯誤形狀的成本
把資料建模成分開的表格,會迫使你用 Scan 或用戶端 join 來重新組合出一個顧客,而那正是 Scan 陷阱。先建模存取模式,再設計鍵,讓每一種模式都成為一個 Query。(至於那個你從未建模到的臨機跨實體問題,DynoTable 的 SQL Workbench 能在用戶端執行 JOIN——探索不必等一次重新建模。)
用免費的單一資料表設計工具勾勒設計本身——它會把你的存取模式清單變成 PK/SK/GSI 規劃,附上範例項目與成本提示。 用項目大小與容量計算機估算這些項目每次讀取的成本,並試用 DynoTable,即可瀏覽一個單一表格 schema,並排看到那些重載的集合。