DynamoDB 複合主索引鍵:分割區索引鍵與排序索引鍵詳解
複合主索引鍵是兩個屬性:一個分割區索引鍵和一個排序索引鍵。分割區索引鍵決定一個項住在哪裡;排序索引鍵給那個分割區內部的各項排序。
從 SQL 過來,與其把它想成一個唯一的 id 列,不如把它想成 GROUP BY partition, ORDER BY sort 被烤進了表本身。
什麼是 DynamoDB 複合主索引鍵?
DynamoDB 複合主索引鍵由兩個屬性組成:一個分割區索引鍵和一個排序索引鍵。分割區索引鍵決定一個項存放在哪個物理分割區中;排序索引鍵給那個分割區內部的各項排序。兩者共同構成項的唯一標識,並讓一次 Query 返回一個排好序的範圍,而非單個項。
- 兩部分,兩份活。 分割區索引鍵把項路由到一個物理分割區;排序索引鍵給共享那個分割區索引鍵的每一個項排序。
- 唯一性是這一對。 只要排序索引鍵不同,兩個項就可以共享一個分割區索引鍵值 —— 這就是一個分割區如何容納許多行。
- 排序索引鍵是整個要點。 是它讓一次
Query返回一個範圍(>=、between、begins_with)而非一個項,且不需Scan。 - 鍵必須是標量。 分割區索引鍵和排序索引鍵只能是字串、數字或二進位制 —— 沒有對映,沒有列表(AWS 文件)。
簡單鍵 vs 複合鍵
一個簡單主索引鍵就只是一個分割區索引鍵。它唯一地標識一個項,而你用 GetItem 把它讀回來。僅此而已 —— 沒有範圍讀取,沒有“給我最新的 N 個”。
複合鍵加上排序索引鍵,而那單單一項新增,就是讓 DynamoDB 感覺像個資料庫、而非一張雜湊表的東西。
| 簡單鍵 | 複合鍵 | |
|---|---|---|
| 屬性 | 僅分割區索引鍵 | 分割區索引鍵 + 排序索引鍵 |
| 唯一性 | 分割區索引鍵值 | 這兩個值組成的對 |
| 每分割區多項 | 否 | 是 |
Query 一個範圍 | 否(僅 GetItem) | 是(begins_with、between、>) |
| 天然適配 | 按 id 查詢 | 時間序列、一對多、歷史 |
給感測器讀數表建模
假設你從一批現場感測器收集溫度取樣。訪問模式是“在一個時間視窗內,取某個裝置的讀數,最新優先”。那是教科書式的複合鍵。
用裝置 id 作分割區索引鍵、讀數時間戳作排序索引鍵:
| deviceId | readingTs | tempC | humidity |
|---|---|---|---|
| DEV#a1b2 | 2026-06-23T08:00:00Z | 21.4 | 48 |
| DEV#a1b2 | 2026-06-23T08:05:00Z | 21.7 | 47 |
| DEV#a1b2 | 2026-06-23T08:10:00Z | 22.1 | 46 |
| DEV#c9d8 | 2026-06-23T08:00:00Z | 19.8 | 55 |
全部三個 DEV#a1b2 讀數都落在同一個分割區,物理上存在一起,並按 readingTs 排序。
AWS 把分割區索引鍵叫做 雜湊屬性、把排序索引鍵叫做 範圍屬性 —— 排序索引鍵是一個你能在其內部掃描的範圍(AWS 文件)。
下面是各項如何在每個分割區索引鍵之下坍縮成一個項集合:
對分割區索引鍵的一次 Query 讀取那個裝置的每一條讀數,已經按時間戳排好序 —— 用戶端無需排序,無需第二次往返。
該視窗的成本是多少
分割區中每個 2 KB 的 10 個讀數總計為 20 KB 計量。 DynamoDB輪每 4 KB 塊讀取一次,因此該視窗上最終一致的 Query 成本
3 讀取請求單元(20 KB → 5 個 4 KB 塊 × 每個 0.5 RCU)。一個同一視窗上的強一致讀取需要 5 單位(每個塊一個 RCU)。使用十個單獨的 GetItem 呼叫和最小一塊來獲取相同的十行規則適用於每個項目 → 5 單位最終一致,10 強烈一致——在計算額外的往返行程之前。
| 讀取模式 | 項目 | 資料觸及 | EC 讀取單元 |
|---|---|---|---|
開始和結束之間有一個 Query、readingTs | 10 | 10 20 KB | 3 |
10 × GetItem 全複合鍵 | 10 | 10 20 KB | 5 |
Query 僅分割區,在應用程式中過濾溼度 | 10 | 10 20 KB | 3 |
Scan表,過濾器deviceId+時間視窗 | 全部 | 整桌 | 桌子大小 |
原型設計時透過ReturnConsumedCapacity: TOTAL;的
pricing calculator 將單位計數變為一旦您知道每秒的請求數,就會顯示每月的訂單項。
查詢那個範圍,別掃描它
因為 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 運算式構建器視覺化地構建它,並把精確的 KeyConditionExpression 拷進你的 SDK 呼叫。
有意地設計排序索引鍵
排序索引鍵不是免費的後設資料 —— 它是範圍讀取的唯一槓杆,所以把它塑造成貼合你的查詢。
- 用一個可排序的時間戳。 ISO-8601 字串或補零的紀元數字排序正確;原始的本地化日期不行。
- 給它加字首以做一對多過載。 一個像
READING#2026-06-23T08:00:00Z的排序索引鍵讓你在一個分割區下混合實體型別,並用begins_with切分它們。那是通向單表設計的接縫。 - 把高基數維度放進分割區索引鍵。 感測器 id 有數千個值,所以它均勻地分散寫入。一個低基數分割區索引鍵(比如
region)會造出一個熱分割區。
每秒 10,000 次寫入時僅具有 50 個不同值的分割區索引鍵在 DynamoDB 分割之前,流將每個邏輯分割區集中約 200 個 WCU — 在原型規模上還好,在生產規模上卻很痛苦。更喜歡這樣的識別符號透過粗略分組與您的裝置群(裝置 ID、租戶 ID、會話 ID)一起增長除非你故意並置一個有界資料集。
複合鍵何時咬你
它是一項承諾,而非一項便利。陷阱:你挑了一個分割區索引鍵、上線了,然後發現一個需要 不同 分組的訪問模式 —— “整個機隊裡所有高於 30°C 的讀數”。
基表回答不了那個;分割區索引鍵是固定的。你的選項是一個帶不同鍵的全域次要索引,或者重構。
在你提交鍵模式 之前 把你的讀取列舉出來。改一個主索引鍵意味著一次表遷移,而非一次 ALTER TABLE。
在 DynoTable 中,開啟表瀏覽器並檢查複合鍵表並排與其 GSI 並排 — 排序索引鍵上的排序順序逐行可見,其中使相差一的時間戳格式在投入生產之前變得顯而易見。
後續步驟
複合鍵是項集合、一對多關係和大多數有用的索引設計之下的根基 —— 接下來讀單表設計和 GSI 對比 LSI,看它們通向何處。
在 DynamoDB 運算式構建器裡勾勒你的 KeyConditionExpression,在查詢構建器裡生成完整的分頁 Query,然後試用 DynoTable,去瀏覽你真實的分割區,看排序順序對著你自己的表對齊起來。