入門閱讀時間 3 分鐘

DynamoDB 複合主鍵

複合主鍵是兩個屬性:一個分割鍵和一個排序鍵。分割鍵決定一個項目住在哪裡;排序鍵把那個分割裡的項目排序。

從 SQL 過來,與其把它想成一個唯一的 id 欄,不如把它想成 GROUP BY partition, ORDER BY sort 直接烤進了表本身。

什麼是 DynamoDB 複合主鍵?

DynamoDB 複合主鍵結合兩個屬性:一個分割鍵和一個排序鍵。分割鍵決定一個項目存放在哪個實體分割;排序鍵把那個分割裡的項目排序。兩者合起來構成項目的唯一身分,並讓單一次 Query 回傳一個排好序的範圍,而不是單一個項目。

  • 兩個部分,兩個職責。 分割鍵把項目導向一個實體分割;排序鍵把共用那個分割鍵的每一個項目排序。
  • 唯一性是那一對。 只要排序鍵不同,兩個項目就能共用一個分割鍵值——一個分割就是這樣裝下許多列。
  • 排序鍵才是整個重點。 它讓一次 Query 回傳一個範圍(>=betweenbegins_with)而不是一個項目,沒有 Scan
  • 鍵必須是純量。 分割鍵和排序鍵只能是字串、數字或二進位——沒有映射、沒有列表(AWS 文件)。

簡單鍵 vs 複合鍵

簡單主鍵只是一個分割鍵。它唯一地識別一個項目,你用 GetItem 把它讀回來。就這樣——沒有範圍讀取,沒有「給我最新的 N 個」。

複合鍵加上排序鍵,而那單一個加法,就是讓 DynamoDB 感覺像個資料庫、而不是個雜湊表的關鍵。

簡單鍵複合鍵
屬性只有分割鍵分割鍵 + 排序鍵
唯一性分割鍵值一對
每分割多個項目
Query 一個範圍否(只能 GetItem是(begins_withbetween>
天然合適依 id 查找時間序列、一對多、歷史

建模一張感測器讀數表

假設你從一隊現場感測器蒐集溫度樣本。存取模式是「取得某個裝置在某個時間窗內的讀數,最新優先」。那是教科書般的複合鍵。

用裝置 id 當分割鍵,讀數時間戳當排序鍵

deviceIdreadingTstempChumidity
DEV#a1b22026-06-23T08:00:00Z21.448
DEV#a1b22026-06-23T08:05:00Z21.747
DEV#a1b22026-06-23T08:10:00Z22.146
DEV#c9d82026-06-23T08:00:00Z19.855

全部三筆 DEV#a1b2 讀數落在同一個分割,實體上儲存在一起,並依 readingTs 排序。

AWS 把分割鍵稱為雜湊屬性(hash attribute),把排序鍵稱為範圍屬性(range attribute)——排序鍵是一個你能在其內掃描的範圍(AWS 文件)。

以下是這些項目如何在每個分割鍵之下塌縮成一個項目集合:

分割:DEV#a1b2readingTs 08:00readingTs 08:05readingTs 08:10Query deviceId = DEV#a1b2

針對分割鍵的一次 Query 讀出那個裝置的每一筆讀數,而且已經按時間戳排好——不必在用戶端排序,不必第二趟往返。

查詢範圍,別掃描它

因為 readingTs 是一個 ISO-8601 字串,它按字典序排序的方式,跟它按時序排序的方式相同。所以一次時間窗讀取是一個鍵條件範圍,不是一個過濾:

Query
deviceId  = "DEV#a1b2"
readingTs BETWEEN "2026-06-23T08:00:00Z" AND "2026-06-23T08:10:00Z"

那是一個 KeyConditionExpression——它在 DynamoDB 回傳資料之前就把讀取縮小,所以你只為窗內的項目付費。一個 FilterExpression 在讀取之後才執行,並為它掃過的一切向你收費;那就是迷你版的 Scan 地雷

那個表達式本身,帶著佔位符與帶類型的值,手寫起來很麻煩。用 DynamoDB Expression Builder 視覺化地建好它,並把確切的 KeyConditionExpression 複製進你的 SDK 呼叫。

刻意地設計排序鍵

排序鍵不是免費的中介資料——它是範圍讀取的唯一槓桿,所以要依你的查詢來塑形它。

  • 用一個可排序的時間戳。 ISO-8601 字串或補零的 epoch 數字會正確排序;原始的本地化日期不會。
  • 為一對多超載加上前綴。 一個像 READING#2026-06-23T08:00:00Z 的排序鍵,讓你在一個分割之下混放實體類型,並用 begins_with 切片。那是通往單表設計的接縫。
  • 把高基數的維度放進分割鍵。 感測器 id 有數千個值,所以它把寫入均勻分散。一個低基數的分割鍵(比如 region)會造出一個熱分割。

複合鍵什麼時候會咬你

它是一個承諾,不是一個方便。陷阱是:你挑了一個分割鍵、上線了,然後發現一個需要不同分組的存取模式——「整隊裡所有高於 30°C 的讀數」。

基礎表回答不了那個;分割鍵是固定的。你的選項是一個帶不同鍵的全域次要索引,或重建結構。

在你提交鍵綱要之前,列舉你的讀取。改變一個主鍵意味著一次表遷移,不是一個 ALTER TABLE

下一步

複合鍵是項目集合、一對多關係和大多數有用的索引設計底下的根基——接著讀單表設計GSI 與 LSI,看看它們通往何處。

DynamoDB Expression Builder 裡草擬你的 KeyConditionExpression,然後試用 DynoTable,去瀏覽你真實的分割,看著排序順序對著你自己的表對齊。

已更新