進階閱讀時間 3 分鐘

DynamoDB 實體分區

一個實體分區 是 DynamoDB 實際用來儲存你資料的單位:一片 SSD,跨多個可用區複製,持有你鍵空間的一片。 你的資料表是一個邏輯上的東西。分區才是位元組——和吞吐量 上限——真正所在之處。

DynamoDB 分區如何運作?

DynamoDB 將你的資料表分散儲存於實體分區——跨可用區複製的 SSD 切片。每個分區封頂在約 10 GB、3,000 讀取單位/秒,以及 1,000 寫入單位/秒。你的分區鍵雜湊決定項目落在哪個分區,而 DynamoDB 會在分區成長或變熱時自動分裂它們。

  • 每個分區封頂在約 10 GB 儲存、3,000 讀取單位/秒,以及 1,000 寫入單位/秒。 那些上限是每分區的,不是每資料表的。
  • 你分區鍵的雜湊挑選分區。 同一個鍵的項目會 落在一起;一個熱鍵就意味著一個熱分區。
  • DynamoDB 替你分裂分區——依大小,以及依持續的熱度——但 它修不了一個把所有流量漏斗到單一處的鍵。
  • 容量還有餘裕卻節流就是徵兆。 你的資料表坐在 5% 使用率時 出現 ProvisionedThroughputExceeded 錯誤,意味著單一個分區已經滿載。

一個項目如何找到它的分區

DynamoDB 把你的分區鍵值餵進一個內部雜湊函式。雜湊 輸出挑選實體分區。同一個鍵進,同一個分區出——每次都一樣。

從 SQL 過來,沒有類比物。沒有你調的索引 B-tree、沒有你 親手指派的分片鍵。擺放位置是一個你不控制、也永遠看不見的雜湊。

共用一個分區鍵的項目組成一個項目集合,存在一起並 依排序鍵排序。那正是為什麼對單一個鍵的 Query 很便宜——它讀的是 一個分區上的一段連續區間。(參見 Query 與 Scan。)

拿一個遊戲的對戰事件儲存。資料表的鍵是 arenaId(分區)和 eventKey(排序):

# Item
arenaId    = "ARENA#7f3a"
eventKey   = "EVT#1719100800#a91c"
playerTag  = "Nightjar"
dmgDealt   = 412

競技場 7f3a 的每個事件都雜湊到同一個分區,並依排序鍵 順序堆疊。對「讀這場對戰的時間軸」很棒。如果那一個競技場拿走 所有流量,就是一個負債。

每個分區強制的三道上限

單一個分區被設計成最多遞送:

上限每分區計為
儲存~10 GB原始項目位元組
讀取容量3,000 讀取單位/秒1 RU = 一次 4 KB 強一致讀取
寫入容量1,000 寫入單位/秒1 WU = 一次 1 KB 寫入

來源:AWS《Best practices for designing partition keys》指南。

項目大小放大這道算式。一個 20 KB 的項目每次強一致讀取要花 5 個讀取單位,所以一個分區在節流前服務約 600 次這種讀取/秒—— 而不是 3,000。寫入成本每 1 KB 進位,讀取成本每 4 KB 進位。

陷阱:這些是 分區 上限,不是 資料表 上限。你的資料表可以 為 40,000 WCU 預配卻仍然節流,因為所有寫入都在捶 一個頂在 1,000 的分區。

分區如何分裂

DynamoDB 在兩種情況下自動增加分區。你永遠不會跑一個指令。

依大小分裂。 當一個分區填向約 10 GB,DynamoDB 把它的鍵 範圍一分為二,並把一半的項目移到一個新分區。儲存 透明地增長;你的讀取與寫入全程持續運作。

依熱度分裂。 當一個分區承受接近其吞吐量上限的持續 流量,DynamoDB 分裂那個熱鍵範圍,讓每一半落在它自己的分區上。 AWS 稱這個為 熱度分裂(split-for-heat) 機制。會自行停止的短暫 節流爆發,通常意味著剛剛發生了一次分裂。

大小熱度分區 A~10 GB /分裂觸發?依儲存位元組把範圍對半切依流量把範圍對半切兩個分區各有自己的容量一個熱鍵仍在一個分區上

分裂在許多鍵之間買到空間,但底部那個節點是陷阱:一次分裂 劃分一個 鍵範圍,從不劃分單一個鍵。

為什麼一個熱鍵打敗分裂器

這就是地雷。分裂重新分布分區鍵的 範圍。如果你的 流量集中在一個鍵值上,每個請求都雜湊到同一個分區, 而且沒有範圍剩下可劃分。

如果競技場 7f3a 是一場拉進 4,000 寫入/秒的錦標賽決賽、而其他每個 競技場都閒置,你會在 1,000 節流——而分裂幫不上忙,因為這 4,000 全都共用一個鍵。較新的 KeyRangeThroughputExceeded 節流原因正點名 了這個:是一個分區的鍵範圍超過了它的上限,不是資料表。

修法在資料模型裡,不在容量滑桿裡。寫入分片 那個熱鍵: 附上一個小後綴,讓一個邏輯競技場散布到 N 個實體分區上。

arenaId = "ARENA#7f3a#3"   # shard 0..9, chosen per write

讀取接著扇散到各分片並在用戶端合併。你可以在 DynamoDB 運算式建構器裡,先為每個分片 打樣鍵的形狀和 Query,再去動一行 應用程式碼。

一個細節:LSI 例外

有一種情況下,儲存 確實 是每分區鍵封頂的。沒有 Local Secondary Index 時,一個項目集合會依需要自動分裂到任意多個分區上—— 數十億個排序鍵值都沒問題。

加一個 LSI,那麼單一個分區鍵的整個集合就必須塞進一個 10 GB 分區裡,因為 LSI 共用它。那就是在 GSI 與 LSI 裡涵蓋的每 PK 懸崖——這也是為什麼大多數團隊伸手去拿 GSI 的另一個理由。

設計成讓分區保持涼快

你實際控制的那根槓桿是分區鍵。挑一個相對於列數有許多各不相同 值的鍵,讓流量均勻分散。(更多模式在 單一資料表設計。)

  • 高基數鍵。 一個每使用者或每租戶的鍵,勝過一個大家 同時猛捶的每日或每狀態的鍵。
  • 留意已知的熱鍵。 一個「當前錦標賽」或「今天」的值,是 在你上線 之前 就要看出的集中風險,不是之後。
  • 分片那個無可避免的熱鍵。 當一個鍵必須承受超大流量時,一個 後綴是標準的逃生口。

容量還有餘裕卻節流,是你判斷某個分區是熱的訊號。檢查 那些惹事的項目,並在 DynoTable 裡演練一次分片的鍵布局—— 把它指向你自己的資料表、找出那個超載的鍵, 並在它半夜呼叫你之前先把修法建好模。

已更新