💡 先搞懂問題
虛構的「晴空食品」原本把線上訂單放在關聯式資料庫,促銷時每秒湧進上千筆下單,資料庫的寫入撐不住,加大執行個體又很貴。團隊決定把訂單搬到 Amazon DynamoDB:它是全受管、無伺服器的鍵值與文件資料庫,沒有執行個體要選,個位數毫秒的延遲,容量可以自動擴充。
真正動手後,大家卡在同一件事:DynamoDB 不像 SQL 那樣「想用哪個欄位查就用哪個」。建立資料表(table)時一定要決定主索引鍵(primary key),它由分割區索引鍵(partition key)與選用的排序索引鍵(sort key)組成;之後最有效率的查詢方式 Query,一定要給出分割區索引鍵的值。用不在主索引鍵裡的欄位查,只能建立次要索引(secondary index),或用 Scan 把整張表讀一遍。另一個常見的坑是熱分割區(hot partition):選了一個只有少數幾個值的欄位當分割區索引鍵,例如訂單狀態,所有新訂單都擠進同一個分割區,再大的容量也幫不上忙。最後是帳單:讀取容量單位(RCU)與寫入容量單位(WCU)怎麼算、選隨需(on-demand)還是佈建(provisioned)容量模式。
生活比喻:連鎖圖書館的分館與書架編號
想像一個城市的公共圖書館有很多分館。為了讓每間分館的工作量差不多,總館規定:一本書要放在哪個分館,由書的「分類號」經過一套固定的換算決定,讀者自己不能選。到了分館裡,書架上的書再依「入館日期」排好。這樣一來,你想找某個分類、某段期間進館的書,只要算出是哪間分館,走到那一排書架,從起點一路拿到終點就好,又快又不用翻其他地方。
麻煩出在兩種情況。如果有人想找「所有封面是藍色的書」,因為分館與書架都不是依顏色排,只能派人走遍每一間分館、每一個書架。另一種是分類號訂得太粗,例如只分成「小說」「非小說」兩類,全城的書都擠進兩間分館,其他分館閒著,那兩間分館的櫃台天天大排長龍。聰明的館方會另外製作一套「依作者編排的目錄」,這套目錄也照同樣的規則分到各分館,只是新書入館後,目錄要過一下子才會更新。
這個比喻有三個地方和實際不同。第一,真實的分館數量是固定的,DynamoDB 的分割區數量則由服務依資料量與吞吐量自動增加,你看不到也不能指定;官方說明每個分割區最多約 10 GB,每秒最多 3,000 個讀取單位與 1,000 個寫入單位。第二,分類號在圖書館裡通常有意義,DynamoDB 的雜湊只負責分散,相鄰的鍵值(例如 C022 與 C023)多半落在不同分割區,所以「依分割區索引鍵排序」或「查 C020 到 C029」是做不到的,範圍條件只能用在排序索引鍵。第三,圖書館的目錄可以隨時加,DynamoDB 的 GSI 也能在建表之後再加,但另一種本機次要索引(LSI)只能在建立資料表時一起定義,之後不能補。
🎮 互動實驗室一:分割區鍵挑選模擬
替虛構的「晴空食品」訂單資料表挑選分割區索引鍵與排序索引鍵。每筆訂單有五個屬性:客戶編號 customerId(約 60 位常客)、訂單日期 orderDate、訂單狀態 status(新訂單一律是 PENDING)、門市編號 storeId(五家門市,S01 旗艦店佔七成)、訂單編號 orderId(每筆都不同)。選好之後,左邊的長條圖模擬促銷尖峰每秒 1,500 筆新訂單寫入時,各分割區分到多少寫入量,紅色虛線是單一分割區每秒 1,000 WCU 的上限;右邊逐一檢查五種常見查詢能不能用 Query。分割區數量固定畫成 8 個、資料與流量都是示意。挑戰:找出一組主索引鍵,讓每筆訂單都有唯一的鍵、沒有熱分割區,而且「查某位客戶的訂單」與「查某位客戶某段期間的訂單」都能直接用 Query。
分割區索引鍵(Partition key)
排序索引鍵(Sort key,選用)
尖峰時各分割區的寫入量(每秒 WCU,示意)
資料表裡的前幾筆項目(依目前的鍵設計排列,示意):
GSI 和 LSI 差在哪裡
右邊標示「需要 GSI」的查詢,代表主索引鍵沒有涵蓋它。全域次要索引(GSI)可以用任何屬性當自己的分割區索引鍵與排序索引鍵,等於依另一種方式把資料重新分一次;建表時或建表後都能新增,每張表預設最多 20 個,有自己的讀寫容量,但只支援最終一致讀取。本機次要索引(LSI)的分割區索引鍵必須和資料表相同,只是換一個排序索引鍵;它只能在建立資料表時定義,每張表最多 5 個,可以用強一致讀取,和資料表共用容量,而且同一個分割區索引鍵值的資料(項目集合)加上索引最多 10 GB。所以「依門市查訂單」要用 GSI;「同一位客戶的訂單改依金額排序,而且要強一致讀取」才是 LSI 的用途,而且要在建表時就想好。
每個分割區每秒 3,000 RCU、1,000 WCU,GSI 與 LSI 的差異與數量上限,依 Amazon DynamoDB 開發人員指南「分割區索引鍵設計」與「次要索引」(查證時間 2026 年 10 月)。DynamoDB 另有自適應容量(adaptive capacity)可以短暫吸收不平均的流量,但不能取代好的鍵設計。
🎮 互動實驗室二:容量計算器
選一個情境或自己輸入尖峰時每秒的讀取與寫入次數、項目大小,以及讀取一致性與寫入類型。右邊會依官方規則算出佈建模式需要的 RCU 與 WCU,並用示意單價比較同一個月用佈建模式與隨需模式的成本。示意單價的單位是「點」,只保留兩種模式之間大致的比例,不是實際價格,實際價格請以 Amazon DynamoDB 定價頁為準。「平均使用率」是整個月的平均流量佔尖峰的比例:佈建模式要依尖峰保留容量並按小時計費,隨需模式只依實際請求計費。
一、輸入(尖峰時)
| 示意單價(點) | 讀取 | 寫入 |
|---|---|---|
| 佈建:每單位每小時 | 0.0013 | 0.0065 |
| 隨需:每 100 萬請求單位 | 1.25 | 6.25 |
二、計算結果
每次寫入 = ⌈項目 KB ÷ 1⌉ × 1(標準)/2(交易)
RCU、WCU = 每秒次數 × 每次單位,無條件進位
計算規則依 Amazon DynamoDB 開發人員指南「讀取與寫入作業」與「佈建容量模式」(查證時間 2026 年 10 月):強一致讀取每 4 KB 1 個單位、最終一致讀取減半、交易讀取加倍;標準寫入每 1 KB 1 個單位、交易寫入加倍;單一項目最大 400 KB。隨需模式依讀取與寫入請求單位計費,單位的算法相同。示意單價為本頁自訂的比例。
🛠️ 操作教學:建立資料表、GSI 與全域資料表複本
不用登入 AWS,也能先把流程走一遍。左邊是簡化的管理主控台,照步驟填表、按按鈕;右邊同步顯示等效的 AWS CLI 與 CloudFormation 範本,黃色底的那幾行就是目前這一步對應的內容。你可以故意填錯看看驗證訊息,也可以改資料表名稱、索引鍵名稱與類型、容量模式,看指令怎麼跟著變。預設值沿用實驗室一找到的設計:分割區索引鍵 customerId、排序索引鍵 orderKey(日期#訂單編號)。
create-table 一次把 GSI 也建好,也適合寫成腳本反覆執行,例如每天匯入資料後跑查詢檢查。CloudFormation 把資料表、索引、加密、刪除保護與時間點復原寫成可版本控管的範本,部署成一個堆疊;刪除堆疊時,堆疊建立的資源會一起刪除,但開著刪除保護的資料表會讓刪除失敗,要先把保護關掉。要用範本管理多區域複本,資源類型要改用 AWS::DynamoDB::GlobalTable,原理補完有說明兩者差異。
自己動手時要注意
權限:你的 IAM 身分需要 dynamodb:CreateTable、dynamodb:UpdateTable、dynamodb:DescribeTable、dynamodb:PutItem、dynamodb:Query、dynamodb:UpdateContinuousBackups,練習完還要 dynamodb:DeleteTable。第一次建立全域資料表複本時,DynamoDB 會建立負責複寫的服務連結角色,所以還需要 iam:CreateServiceLinkedRole;選了客戶受管的 KMS 金鑰,則要有使用那把金鑰的權限。應用程式存取資料表時,用 IAM 角色並只開放需要的動作與資料表 ARN。
費用:隨需模式依讀寫請求單位計費,佈建模式依保留的容量按小時計費;另外還有資料儲存量、GSI 本身的讀寫與儲存、時間點復原,以及全域資料表在每個複本區域的複寫寫入與跨區域資料傳輸。2025 年 7 月 15 日以後建立的新帳戶採用新的 Free Tier,以抵用金為主;選「免費方案」的帳戶在 6 個月到期或抵用金用完時會自動關閉。這個練習的資料量很小,但複本區域會持續產生儲存費,練習完請刪除。
刪除:開了刪除保護的資料表,要先關閉保護才能刪;有全域資料表複本的話,先刪除複本。CloudFormation 建立的,先把範本的 DeletionProtectionEnabled 改成 false 重新部署,再刪除堆疊。
# 用 CLI 建立的:先刪複本、關閉刪除保護,再刪除資料表
aws dynamodb update-table --table-name qk-orders --replica-updates '[{"Delete":{"RegionName":"us-west-2"}}]' --region ap-northeast-1
aws dynamodb update-table --table-name qk-orders --no-deletion-protection-enabled --region ap-northeast-1
aws dynamodb delete-table --table-name qk-orders --region ap-northeast-1
# 用 CloudFormation 建立的:範本關閉刪除保護並重新部署後,刪除堆疊
aws cloudformation delete-stack --stack-name qk-orders-stack --region ap-northeast-1
範例裡的帳戶 ID 都用 111122223333 這類佔位字。不要把存取金鑰寫進連線 DynamoDB 的程式碼;在 Lambda、EC2、容器上執行時,一律透過 IAM 角色取得臨時憑證。資料表放的若是個人資料,加密選客戶受管金鑰可以多一層存取控制與稽核紀錄。
📘 原理補完
資料模型與主索引鍵
DynamoDB 的資料表由項目(item)組成,每個項目由多個屬性(attribute)組成,除了主索引鍵以外,每個項目可以有不同的屬性,不需要事先定義結構(schemaless)。單一項目最大 400 KB。主索引鍵有兩種形式:只有分割區索引鍵的簡單主索引鍵,同一個值只能有一個項目;分割區索引鍵加排序索引鍵的複合主索引鍵,同一個分割區索引鍵值可以有很多項目,以排序索引鍵區分並排序。主索引鍵的屬性只能是字串(S)、數字(N)或二進位(B);分割區索引鍵的值最大 2,048 位元組,排序索引鍵最大 1,024 位元組。資料表名稱與索引名稱是 3~255 個字元,只能用英數字、底線、連字號與句點。
設計主索引鍵時,官方建議的順序是先列出存取模式,再決定鍵:應用程式會用哪些條件查、查多頻繁、要不要排序、要不要範圍。排序索引鍵常把多個屬性接起來(例如 2026-10-08#O1088),讓同一個鍵同時支援「依日期範圍查」與「唯一識別」;Query 對排序索引鍵可以用等於、大於、小於、BETWEEN 與 begins_with,對分割區索引鍵只能用等於。
熱分割區與寫入分散
每個分割區每秒最多提供 3,000 個讀取單位與 1,000 個寫入單位,資料表的總容量再大,也無法讓單一分割區超過這個上限。DynamoDB 有自適應容量(adaptive capacity),會把容量往流量大的分割區調度、必要時拆分分割區,能吸收短時間的不平均;但同一個分割區索引鍵值的流量本身就超過上限時,拆分也沒用,因為同一個鍵值的寫入一定落在同一處。解法是一開始就選基數高(值很多)、存取平均的屬性;真的必須用低基數的值時(例如「今天的日期」),可以在鍵值後面加一個隨機或計算出來的後綴(2026-10-08#7),把寫入分散到多個鍵值,這叫寫入分散(write sharding),代價是讀取時要查多個後綴再合併。
Query、Scan 與兩種次要索引
GetItem 給完整的主索引鍵取出一個項目;Query 給分割區索引鍵的值,再選擇性地加上排序索引鍵條件,取出一段連續的項目;Scan 讀遍整張表或整個索引。Query 與 Scan 一次最多讀取 1 MB 的資料,超過要用分頁繼續讀;FilterExpression 在讀取之後才套用,所以篩掉的項目一樣計入讀取量。Query 的讀取量是把回傳項目的大小加總後,再無條件進位到 4 KB 的倍數,所以一次 Query 很多小項目,比一筆一筆 GetItem 省。
| 項目 | 全域次要索引(GSI) | 本機次要索引(LSI) |
|---|---|---|
| 建立時機 | 建表時或建表後都可以新增、刪除 | 只能在建立資料表時定義,之後不能新增或刪除 |
| 索引鍵 | 任何屬性當分割區索引鍵,排序索引鍵選用 | 分割區索引鍵和資料表相同,換一個排序索引鍵 |
| 讀取一致性 | 只有最終一致 | 最終一致或強一致 |
| 容量 | 有自己的讀寫容量(佈建模式要另外設定) | 和資料表共用容量 |
| 大小限制 | 無 | 同一個分割區索引鍵值(項目集合)最多 10 GB |
| 每張表上限 | 預設 20 個 | 5 個 |
| 典型用途 | 用完全不同的屬性查,例如依門市、依訂單編號 | 同一位客戶的資料改用另一種順序,而且需要強一致 |
查證時間 2026 年 10 月,依 Amazon DynamoDB 開發人員指南「使用次要索引改善資料存取」。全域資料表的 MRSC 模式不支援 LSI。
讀取一致性與容量單位
DynamoDB 在一個區域內會把資料複製到多個可用區域。預設的最終一致讀取(eventually consistent read)可能讀到稍早的版本,但只耗一半容量;強一致讀取(strongly consistent read)保證讀到所有已成功寫入的最新資料,只能用在資料表與 LSI,不能用在 GSI。需要多個項目「一起成功或一起失敗」時用交易 API(TransactWriteItems、TransactGetItems),容量加倍。計算時先把項目大小無條件進位:讀取以 4 KB、寫入以 1 KB 為單位,再乘上一致性或交易的倍數,最後乘上每秒次數。隨需模式的讀取與寫入請求單位用一模一樣的算法,差別只在計費方式。
容量模式:隨需還是佈建
隨需模式是預設也是官方建議的模式:不用規劃容量,依實際的讀寫請求計費,流量從零到很高都能自動因應,適合新上線、流量難以預測或起伏大的應用。佈建模式要指定每秒的 RCU 與 WCU,依保留的容量按小時計費,可以搭配自動擴展(Application Auto Scaling,依使用率目標調整),適合流量平穩、可以預測的工作負載,或需要把用量控制在固定上限內的情況;超過佈建容量的請求會被節流。兩種模式可以切換:佈建切到隨需,每 24 小時內最多 4 次;隨需切回佈建則隨時可以。
| 比較 | 隨需(On-demand) | 佈建(Provisioned) |
|---|---|---|
| 計費 | 依讀寫請求單位 | 依保留的 RCU、WCU 按小時 |
| 容量規劃 | 不需要 | 要設定容量,建議搭配自動擴展 |
| 適合流量 | 難以預測、起伏大、剛上線 | 平穩、可預測、使用率高 |
| 超過時 | 自動因應(仍受分割區與帳戶上限) | 超過佈建容量會被節流 |
| 切換 | 佈建 → 隨需:每 24 小時最多 4 次;隨需 → 佈建:不限 | |
全域資料表:多個區域都能讀寫
全域資料表(global tables)把一張資料表複寫到多個區域,每個區域的複本(replica)都可以讀也可以寫(多主動,multi-active),讓各地使用者就近存取,也讓應用程式在某個區域故障時改用其他區域。現行版本是 2019.11.21,舊的 2017.11.29 版已列為 legacy。一致性模式有兩種,建立後不能更改:預設的多區域最終一致(MREC)以非同步方式複寫,通常一秒內到達其他複本,同一個項目在兩地同時被修改時,以最後寫入者為準(last writer wins),會自動啟用 DynamoDB Streams;多區域強一致(MRSC)必須剛好三個區域(三個複本,或兩個複本加一個見證),RPO 為零,但不支援 TTL、LSI 與交易 API,而且只能從空的資料表轉換。把一張既有的單區域資料表加上複本(MREC),在主控台是「全域資料表」分頁的「建立複本」,在 CLI 是 update-table --replica-updates。
CloudFormation:Table 和 GlobalTable 的差別
操作教學的範本用 AWS::DynamoDB::Table,它描述的是單一區域的資料表。要用範本管理多區域的複本,要改用 AWS::DynamoDB::GlobalTable:它有 Replicas 清單,必須包含部署堆疊的那個區域,刪除保護、時間點復原等設定寫在每個複本底下;有兩個以上的複本時要提供 StreamSpecification;計費模式選佈建時,寫入容量只能用自動擴展設定(WriteProvisionedThroughputSettings),不能直接寫固定值。官方文件特別提醒:不要把既有的 Table 資源直接改成 GlobalTable 型別,那可能導致資料表被刪除;應該建立新的 GlobalTable 資源,一開始可以只有一個區域,之後再加複本。下面這份範本通過 cfn-lint 檢查:
AWSTemplateFormatVersion: '2010-09-09'
Description: Orders global table (MREC) in two Regions
Resources:
OrdersGlobalTable:
Type: AWS::DynamoDB::GlobalTable
Properties:
TableName: qk-orders-global
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: customerId
AttributeType: S
- AttributeName: orderKey
AttributeType: S
KeySchema:
- AttributeName: customerId
KeyType: HASH
- AttributeName: orderKey
KeyType: RANGE
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
Replicas:
- Region: !Ref AWS::Region
DeletionProtectionEnabled: true
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
- Region: us-west-2
DeletionProtectionEnabled: true
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
判斷步驟
- 先列出所有存取模式:用什麼條件查、多頻繁、要不要排序與範圍。需要任意條件查詢、JOIN、臨時報表,改用 RDS/Aurora,或把資料匯出到 S3 用 Athena 分析。
- 選分割區索引鍵:值很多、存取平均,而且最常用的查詢都能給出它的值(例如客戶編號)。
- 選排序索引鍵:要做範圍查詢的屬性放前面,必要時接上唯一識別(例如 日期#訂單編號),確保主索引鍵唯一。
- 主索引鍵涵蓋不到的查詢建 GSI;需要同一個分割區索引鍵、另一種排序又要強一致,在建表時就定義 LSI。
- 計算 RCU、WCU:項目大小無條件進位(讀 4 KB、寫 1 KB),再看一致性與交易倍數;流量難預測用隨需,平穩且使用率高用佈建加自動擴展。
- 要多區域就近讀寫或區域容錯,用全域資料表(預設 MREC);要求零 RPO 再評估 MRSC 的限制。
- 正式環境開啟刪除保護與時間點復原;讀取密集、需要微秒延遲的情境,再評估 DAX。
容易考錯的地方
分割區索引鍵可以之後再改:不行。主索引鍵在建立資料表時就固定,要換只能建新表並搬資料;GSI 可以後加,LSI 也只能建表時定義。
熱分割區就加容量:單一分割區有每秒 3,000 RCU、1,000 WCU 的上限,問題出在鍵值太集中時,加總容量沒有用,要改鍵設計或做寫入分散。
RCU 算錯:項目大小一定先無條件進位;最終一致是強一致的一半,交易是兩倍。7 KB 的強一致讀取是 2 RCU,不是 1.75。
GSI 可以強一致讀取:不行,GSI 只有最終一致;強一致只能用在資料表與 LSI。題目要求「依其他屬性查詢而且一定讀到最新值」,就要回頭思考鍵設計或是否該用 GSI。
FilterExpression 能省容量:不能,它在讀取之後才套用。要省讀取量,靠的是 Query 的鍵條件與好的鍵設計,不是篩選條件。
全域資料表等於讀取複本:不一樣。DynamoDB 的全域資料表每個複本都能寫,是多主動;RDS 的讀取複本是唯讀的。要多區域低延遲寫入,答案是全域資料表;只要讀取快取,答案是 DAX。
相關考試:CLF-C02、SAA-C03、DVA-C02 的 in-scope 服務都包含 Amazon DynamoDB;DVA-C02 的領域任務直接點名分割鍵、一致性模型、Query 與 Scan、DynamoDB 索引。
✅ 自我檢測
6 題原創題,選完立即顯示對錯與解析,全部作答後會出現總分。目前得分:0 / 6