入門閱讀時間 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 把分割區索引鍵叫做 雜湊屬性、把排序索引鍵叫做 範圍屬性 —— 排序索引鍵是一個你能在其內部掃描的範圍(AWS 文件)。

下面是各項如何在每個分割區索引鍵之下坍縮成一個項集合:

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

對分割區索引鍵的一次 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 讀取單元
開始和結束之間有一個 QueryreadingTs1010 20 KB3
10 × GetItem 全複合鍵1010 20 KB5
Query 僅分割區,在應用程式中過濾溼度1010 20 KB3
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,去瀏覽你真實的分割區,看排序順序對著你自己的表對齊起來。

已更新