DynamoDB 分區鍵如何運作
你的分區鍵不是一欄——它是一個位址。DynamoDB 對那個鍵做雜湊, 而雜湊決定哪台實體機器儲存這個項目。鍵挑得好,負載就分散; 挑得差,就有一台伺服器扛下所有壓力。
DynamoDB 分區鍵是如何運作的?
DynamoDB 會將你的分區鍵丟進一個內部雜湊函式,而那個雜湊決定哪個實體分區儲存該項目。鍵不像 SQL 欄位那樣被排序或建立索引——它是一個位址。選一個高基數的鍵,負載就分散到多個分區;選一個低基數的鍵,單一分區就扛下所有壓力。
- 鍵是被雜湊的,不是被排序的。 DynamoDB 把你的分區鍵丟進一個 內部雜湊來選擇分區。兩個相鄰的值在磁碟上落得天差地遠。
- 一個分區是一個真實的儲存單位。 每個約上限在 10 GB、3,000 讀取單位/秒、1,000 寫入單位/秒。你的流量被你的鍵 分散到的分區數量除掉。
- 熱鍵是地雷。 把大多數請求漏斗到單一分區鍵值,你就會在那個 分區上節流,而資料表其餘部分卻閒置。
- 高基數鍵勝出。 你擁有越多各不相同、均勻命中的鍵值, 就有越多分區吸收負載。
從鍵實際在做什麼開始
從 SQL 過來,主鍵是一個排序、有索引的欄,你在它上面 JOIN、ORDER BY。在 DynamoDB 裡,分區鍵(有時叫雜湊鍵)做的是
另一件事:它決定 擺放位置。
DynamoDB 把分區鍵餵進一個內部雜湊函式。輸出對應到 一個鍵空間,而鍵空間被切成許多範圍——每個範圍由一個 實體分區擁有。那個分區是某個真實節點上的真實儲存。
所以分區鍵回答一個問題:哪台機器握有這個項目? 排序鍵(如果你有的話)只在那台機器 之內 排序項目。它 在擺放位置上不起作用。
跟著一次寫入穿過雜湊
假設你經營一個吃進裝置讀數的 SaaS。你的資料表 SensorReadings 用
分區鍵 deviceId 和排序鍵 readingTs。你為
deviceId = "vac-7741" 寫入一筆讀數。
這是那次寫入走的路徑——從你的鍵到它落腳的磁碟:
vac-7741 的寫入被雜湊到鍵空間裡的一點,那一點落在
P2 的範圍裡,於是項目落腳在 P2——在那裡依 readingTs 排序。
要內化的東西是:"vac-7741" 和 "vac-7742" 只差一個字元,
但它們的雜湊毫無關聯。它們幾乎肯定住在不同的
分區上。在分區鍵空間裡沒有「附近」這回事。
這是 DynamoDB 從原始設計繼承來的一致性雜湊概念—— 2007 年的 Amazon Dynamo 論文(「Dynamo: Amazon's Highly Available Key-value Store」)正是藉由雜湊把鍵分散到各節點,讓沒有任何單一節點變成 瓶頸。
尊重分區的硬性上限
一個實體分區是有限的。根據 AWS DynamoDB 開發人員指南,每個約 最多容納:
| 上限 | 每分區 |
|---|---|
| 儲存 | ~10 GB |
| 讀取吞吐量 | 3,000 讀取單位/秒 |
| 寫入吞吐量 | 1,000 寫入單位/秒 |
當一個分區填過 10 GB,或你預配的吞吐量需要更多空間時, DynamoDB 會分裂它——鍵空間範圍被劃分,項目重新分布 到更多分區上。這是自動的;你不會觸發它。
要注意的點:分裂是依項目的 雜湊 來分散。如果每個熱請求都鎖定 單一分區鍵值,分裂幫不上忙——那些流量仍然全部雜湊 到同一個點。你沒辦法把單一鍵的負載分裂到多個分區上。
點名這個陷阱:熱分區
熱分區 是經典的地雷。當一個分區鍵值(或極少數一組) 吸走不成比例的流量份額時,它就發生。
具體的失敗:你把 SensorReadings 換成一個分區鍵 region,
值像是 "us-east"、"eu-west"。三個區域意味著三個鍵值意味著——
頂多——三個分區在做真正的工作。用讀取猛轟 "us-east",它就會
在 3,000 RCU 節流,而資料表的總預配容量卻閒置著。
DynamoDB 的自適應容量緩和了這個——它能把未使用的吞吐量 挪向繁忙的分區,並把單一極熱的鍵隔離到它自己的 分區。AWS 在 re:Invent「Advanced Design Patterns for DynamoDB」深度講座裡詳細說明過。但自適應容量買到的是時間,不是 豁免:它無法細分單一個別鍵的負載。為分散而設計;別倚賴 那張安全網。
選一個高基數的鍵
修法是基數——各不相同鍵值的數量,以及流量 命中它們有多均勻。
- 低基數(
region、status、true/false):分區少、 流量集中、你很早就節流。 - 高基數(
deviceId、userId、一個訂單 ID):許多值雜湊 到許多分區,負載分散,餘裕增長。
從 SQL 過來,你會樂於為一欄 status 建索引並在它上面篩選。當作
DynamoDB 的分區鍵那就是個陷阱——它沒法分散。把低基數
屬性留作篩選條件、或留作次要索引的排序鍵,
絕不要當成決定擺放位置的那個東西。
當一個天生不錯的鍵仍然偏斜時——少數幾個鯨魚租戶的流量
超過其餘——加一個後綴把一個邏輯值扇散到 N 個分區,例如
tenantId#3 用於一條分片的寫入路徑。你在讀取時再重新聚合。
要在你的鍵分散後鎖定一個分區 之內 的項目,你會在排序鍵上寫
一個 KeyConditionExpression。你可以在 DynamoDB 運算式建構器
裡對著你自己的 schema 組一個,再把它接進
程式碼:
deviceId = "vac-7741" AND readingTs BETWEEN "2026-06-01" AND "2026-06-30"
那會從單一分區讀出一台裝置的六月區間——一個 Query,不是
Scan。分區鍵釘住機器;排序鍵
條件收窄資料列。
陷阱與後續步驟
- 別依在 SQL 裡讀起來順不順來挑鍵。 依它 分散 得如何來挑。 基數第一,查詢方便第二。
- 別以為資料表的總容量就是你每個鍵能用的。 吞吐量是 每分區的;一個熱值可能在資料表看起來閒置時就把你節流。
- 別跟分裂硬碰。 它是自動且雜湊驅動的——你的工作是給它 足夠多各不相同的鍵去分散。
一旦你的鍵乾淨地分散開,下一個決定是如何在一個分區 之內 安排項目——參見單一資料表設計——以及何時 一個次要索引才是某個第二存取模式的 正確工具。
下載 DynoTable瀏覽你真實的分區與鍵 分布,並在一個熱鍵半夜呼叫你之前先發現它。