AWS實名認證 DynamoDB 查詢速度慢優化索引方法
第一章:查詢變慢,先別急著加速
DynamoDB 的速度慢,通常不是「資料很多所以慢」這麼單純。它更像是一種契約:你用什麼方式查,系統就必須用相對應的資料路徑去找。當你的查詢型態(Query / Scan)與資料模型(分區鍵、排序鍵、GSI/LSI)不匹配,就會出現明顯延遲,甚至成本暴漲。
優化索引之前,最重要的是判斷:慢是因為「沒有走對索引」,還是「走對了但被其他因素拖慢」,或是「容量與節流造成延遲」。不同原因對應的解法完全不同。很多團隊在看到指標上升後直接加索引,結果只是增加維護成本,延遲改善有限。
因此本文採用一個務實的流程:先定位瓶頸,再針對訪問模式調整主鍵與索引設計,最後透過容量、投影與一致性設定做微調。
第二章:先釐清 DynamoDB 的查詢路徑
DynamoDB 的索引設計核心在於主鍵與「查詢能否只落在少量分區」。了解這點,你就知道什麼叫真正的快。
2.1 Query 與 Scan 的根本差異
你可能以為兩者都能拿到資料,但在 DynamoDB 裡它們是兩種完全不同的操作:
- Query:必須提供分區鍵(partition key)。它會在單一或少數分區內搜尋,通常是低延遲。
- Scan:會遍歷整張表(或索引)所有資料分片。資料越大,速度越慢,且更容易觸發節流與高成本。
很多「查詢慢」其實是因為某些條件讓應用退回到 Scan,或在程式中不小心用錯操作。第一步就是確認:你的慢查詢到底是 Query 還是 Scan。
2.2 分區鍵決定你能否「快速命中」
在 DynamoDB 中,資料會被映射到分區。分區鍵是分配資料的主要依據。Query 的分區鍵如果能精準選中資料,系統就能少走路;反之如果分區鍵設計無法反映你的主要查詢條件,就可能需要跨大量分區,延遲自然上升。
排序鍵(sort key)則決定你能否在同一分區內進一步篩選範圍(例如時間區間、狀態範圍)。如果排序鍵缺失或與查詢模式無關,那你就只能在分區內拿到更多候選資料,再由應用端過濾,效果不佳。
第三章:慢的典型原因與識別方法
要把優化做對,必須對症下藥。下面是最常見的原因,並附上你可以用哪些訊號去判斷。
3.1 沒命中索引或命中方式低效
AWS實名認證 即使你使用 Query,如果你提供的鍵條件無法利用索引的設計優勢(例如只用分區鍵但沒有合適排序條件、或條件組合需要掃更多範圍),也會慢。
此外,很多團隊的索引會「看起來」合理,但投影欄位、鍵順序或查詢條件不一致,導致 DynamoDB 回傳的資料超過預期,或需要回表取得額外欄位。
3.2 分區鍵熱點(hot partition)
AWS實名認證 你可能會看到延遲時好時壞,或某些時間段特別慢。這往往不是整體容量問題,而是特定分區鍵值被大量請求集中打到。
熱點的常見成因:
- 分區鍵設計過於單調,例如把同一個用戶群或同一個狀態直接做為分區鍵,導致流量集中。
- 新資料集中寫入同一段鍵空間,例如時間序列直接當分區鍵。
- 流量分佈不均但沒有做散列策略。
當熱點存在時,你會看到對應分區的讀節流、延遲上升或重試增加。這類問題需要調整分區鍵或加入「分桶(bucket)」策略,而不是盲目新增索引。
3.3 讀寫容量不足與節流
DynamoDB 可能因為 provisioned 模式的讀/寫容量不足而節流,或因 auto scaling 沒跟上需求峰值導致延遲。即使索引命中良好,容量不足仍會讓延遲變差。
你可以觀察:
- ThrottledRequests / ReadThrottleEvents / WriteThrottleEvents
- ConsumedReadCapacityUnits / ConsumedWriteCapacityUnits
- 表/索引層級的 latency 與 error rate
如果瓶頸是節流,那優先處理容量與速率控制;只有在確認「節流不主要」後,再投入索引重構。
3.4 回傳資料量過大(投影不合理)
索引能幫你快速定位,但不代表回傳內容就輕量。若索引投影欄位不符合你的讀取需求,你可能會:
- 用較大的投影導致讀取單位消耗更高、傳輸更慢
- 只拿到索引欄位卻仍需要再查回表(或在應用端過濾大量資料)
AWS實名認證 投影(projection)的設計非常影響延遲與成本,尤其在「列表」類查詢上。
第四章:優化索引的正確方法:從訪問模式倒推
索引不是用來「加更多欄位」的,而是用來支援你的主要訪問模式。想像你要設計的是一套「查詢語法」:你每次想查什麼,就得在鍵設計上提供對應的路徑。
一個有效的索引優化流程通常包括:列出使用案例 → 抽象查詢條件 → 映射到主鍵/排序鍵 → 決定是否需要 GSI → 選擇投影策略 → 控制成本與維護。
4.1 列出 Top N 查詢與其條件組合
你不需要一次把所有查詢都處理完,只要針對慢且頻繁的查詢,通常能快速獲得收益。
建議你把每個查詢寫成這樣的形式:
- Query 類型:Query 或 Scan
- 固定條件:例如一定有 tenantId、userId、category
- AWS實名認證 範圍條件:例如 createdAt > x、status in (...)、price between ...
- 排序/分頁:例如按 createdAt 倒序、NextToken
- 回傳欄位:只需要哪些欄位用於列表展示
有了這些,你才能判斷該用哪個欄位當分區鍵、哪個欄位當排序鍵。
4.2 用分區鍵承載「最能過濾」的固定條件
分區鍵應該承載你每次查詢都會帶上的固定條件,並且能讓結果落在合理數量的分區。
例如一個常見模式:
- 查詢:某租戶(tenant)下的訂單列表,並按時間排序
- 條件:tenantId 固定;createdAt 做範圍/排序
那麼最自然的設計是:
- 分區鍵:tenantId(或 tenantId + 額外散列)
- 排序鍵:createdAt(或 createdAt + 其他維度做複合)
AWS實名認證 若你反過來設計成以 createdAt 當分區鍵,而每次查詢都是特定 tenantId,你就會很難用 Query 精準命中。
4.3 排序鍵用來支援「範圍、排序與分組」
排序鍵可以讓你在同一分區內用範圍條件(between / > / <)查詢,這通常比在應用端過濾快很多。
例如你常查「tenantId 下的某狀態訂單」,你可以:
- 把 status 作為排序鍵的一部分:例如 sortKey = status + '#' + createdAt
- AWS實名認證 這樣 Query tenantId + status 前綴,就能快速取出該狀態的時間序列
這種「前綴」設計是 DynamoDB 覆蓋多個查詢維度的常用手段,但要小心資料分佈與鍵長度。
第五章:GSI(全域二級索引)如何正確加入
主鍵只能針對一種主要訪問模式。當你有多種查詢型態需要不同鍵組合時,GSI 就派上用場。
5.1 GSI 的代價:寫入會被放大
加入 GSI 的最大代價是維護成本:每次寫入主表,符合 GSI 鍵的資料也要同步更新到 GSI。GSI 不是免費的,它會:
- 增加寫入延遲與成本
- AWS實名認證 增加索引存儲空間
- 可能引入新的熱點(若 GSI 的分區鍵也不均)
所以索引新增要針對「確實需要」的查詢,且最好能讓那個慢查詢改成 Query 並明顯減少回傳資料量。
5.2 以「慢查詢」設計對應的 GSI,而不是以欄位列表設計
很多人在設計 GSI 時會先列欄位,再決定鍵;這常導致「鍵欄位不貼近查詢條件」。更好的方式是以查詢語句倒推。
例如你有一個慢查詢:依 userId 查詢某種狀態下的事件列表,並按時間排序。主表鍵是 orderId,那就不方便用 Query userId。
你可以新增 GSI:
- GSI 分區鍵:userId
- GSI 排序鍵:eventType + '#' + createdAt(或狀態 + '#'+ createdAt)
- 投影欄位:列表顯示所需欄位(避免全量投影)
這樣你的慢查詢就能改成對 GSI 的 Query。
5.3 LSI(如果你用的是本地索引)適合哪些情境
若使用 LSI(本地二級索引),它的排序鍵沿用主表分區鍵的設計,適合「分區內的多視角」。但很多需要不同分區鍵的查詢,其實應用 GSI 更合適。
在實務上,絕大多數「查詢慢」且需要跨不同分區鍵的場景,多半得用 GSI。
第六章:投影策略與讀寫成本的平衡
即便索引命中正確,投影不合理也會造成延遲和成本問題。投影決策看似細節,但往往是差距最大的地方。
6.1 KEYS_ONLY、INCLUDE 與 ALL 的選擇
DynamoDB 支援 GSI/LSI 的不同投影型態:
- KEYS_ONLY:只投影鍵(分區鍵、排序鍵)。最省,但你拿不到顯示用資料。
- INCLUDE:投影鍵 + 你指定的非鍵欄位。常見且通常是最好的折衷。
- ALL:投影所有欄位。開發最快,但成本與延遲風險最大。
列表頁通常只需要少量欄位(例如名稱、狀態、時間、摘要),用 INCLUDE 投影通常能降低讀取資料量,讓 Query 更快。
AWS實名認證 6.2 回表 vs. 直接讀索引:用需求決策
有些團隊會追求「索引把所有欄位都投影」,以避免回表查詢。但如果列表僅需要少量欄位,ALL 投影會讓每次讀取都攜帶多餘資料。
你可以採取這樣的策略:
- 列表:GSI 投影 INCLUDE,盡量只帶必要欄位
- 詳情:再用主鍵回表(GetItem)或用另一條索引支援詳情查詢
這通常在延遲與成本上更均衡。
第七章:處理熱點分區的實戰手法
當你把分區鍵選成了某個熱值(例如某大客戶、某單一狀態、某時間段),DynamoDB 仍能運作,但延遲會受影響。你需要的是「把流量打散到多個分區」,而不是一直加容量。
7.1 分桶(bucket)策略
常用做法是在分區鍵前加上桶編號。思想是把同一個邏輯群組分散成多個物理分區,例如:
- 原本分區鍵:tenantId
- 改為分區鍵:tenantId + '#' + bucketId
寫入時根據 hash 或輪詢策略選擇 bucketId;讀取時需要 Query 多個 bucket,再合併排序。這會增加一些程式複雜度,但能有效降低熱點。
如果你的讀取模式允許,你可以先用時間範圍縮小候選,再合併結果,避免回傳過大。
7.2 分區鍵與排序鍵的組合也能緩解熱點
有時候不是分區鍵本身不均,而是排序鍵讓同一分區內寫入集中在最末端(例如所有事件都用時間做排序,造成某種「寫入集中」。)一般而言,時間作排序鍵仍可行;但若你的分區鍵設計導致單一分區承載過多寫入,仍會熱。
此時需要回到分區鍵設計,確保主要寫入與讀取的鍵組合分散。
7.3 量測後再決定桶數
桶數越多,分區越散,但 Query 可能要掃更多 bucket 才能拿到完整結果;桶數太少就會熱點。
所以桶數需要依流量與讀取需求做估算。你可以從小範圍(例如 4、8、16)逐步驗證延遲與節流,再決定最適區間。
第八章:一致性、分頁與重試:讓速度穩定下來
索引與分區鍵是大方向,但很多「偶爾慢」會被一致性與分頁策略放大。
8.1 最終一致性 vs 強一致性
如果你不需要強一致,你可以使用最終一致性讀取。強一致會增加延遲與資源成本。當查詢本來就接近瓶頸時,切換一致性往往是立刻可感的優化。
不過,這取決於你的業務需求:例如是否允許讀取到最近寫入的延遲。
8.2 分頁(NextToken)設計:避免過深翻頁
如果你每次都用 NextToken 深度翻頁,可能會因為候選資料量與掃描步驟增加而變慢。較好的做法是:
- 讓排序鍵與查詢條件能精準縮小範圍
- 盡量避免「用戶頻繁跳轉到非常後面的頁」
- 必要時引入游標以時間或狀態前綴為界
這與索引鍵的設計高度相關。
8.3 重試策略與批次讀寫
AWS實名認證 當遇到節流錯誤(例如 ProvisionedThroughputExceededException),重試會帶來額外延遲。如果你明確知道是容量因素,重試不是解藥,應回到容量與鍵設計。
同時,若你的讀取模式能批量化(例如一次取多個明細),使用批次 API 能降低網路往返與系統開銷,但要注意批量大小與錯誤處理。
第九章:一個可落地的排查流程(從指標到索引)
下面是一個你可以直接照做的流程。它不追求一次得出結論,而是把不確定性拆開。
9.1 第一步:鎖定慢的查詢與來源
- 挑出最常見的慢操作(按延遲 P95/P99 與次數)
- 確認 API 類型(Query 還是 Scan)
- 確認是否命中某個 GSI(或只查主表)
你要先回答:「到底是哪個查詢」而不是「整體系統慢」。
9.2 第二步:看節流與容量指標
- 觀察 throttling 次數是否上升
- 比較 table 與相关 index 的 ConsumedCapacity
- AWS實名認證 檢查 auto scaling 是否跟得上
如果 throttling 是主因,優先修容量與限流;索引重構可能只是加重寫入成本。
9.3 第三步:看回傳資料量與投影
- 查看 response size 與每次請求的 Items 數量
- 若使用索引但回傳很大,檢查投影是否過寬
- 若需要多次回表才補齊資料,考慮是否要調整投影或增加支援詳情的索引
9.4 第四步:檢查鍵條件是否能精準命中
針對該查詢,把條件寫成鍵條件:
- 分區鍵是否完全等值?
- 排序鍵是否用範圍或前綴?
- AWS實名認證 是否有導致跨太多鍵區間的條件?
如果不能命中,你就已經知道下一步該看 GSI。
9.5 第五步:若需要 GSI,先設計鍵,再選投影
新增 GSI 的順序建議:
- 確定 GSI 分區鍵承載的固定條件(每次都會帶)
- 確定排序鍵承載的範圍/排序維度
- 用複合排序鍵做狀態/類型前綴,支援前綴 Query
- 投影先用 INCLUDE 最小化必要欄位,避免 ALL
設計完成後,再做回歸測試:確定慢操作真的改成 Query、候選資料量下降。
第十章:常見設計錯誤清單(避免踩雷)
下面列的是在真實專案中常見、而且常導致查詢慢的問題。你可以用這份清單快速自檢。
10.1 以欄位「長得像」作為鍵,而不是看訪問模式
很多團隊把分區鍵設成最重要欄位,但卻沒有確認查詢是否每次都帶它、以及結果是否集中到少數分區。
10.2 用 Scan 解決沒有索引的需求
Scan 能工作,但吞吐和成本都會讓你在資料量成長後付出代價。若某個 Scan 很常發生,請把它視作索引欠債。
10.3 GSI 投影過寬導致讀取單位增加
ALL 投影看似省回表,但實務上常讓列表與查詢的延遲變差。尤其當你列表只需要少量欄位時。
10.4 排序鍵沒有反映範圍需求
如果你常查時間區間,但排序鍵不是時間或無法用前綴縮小,就會在分區內拉太多資料。
10.5 熱點分區沒有被識別
延遲如果集中在特定鍵值或特定時段,多半是熱點。熱點修復需要分區鍵/桶策略,而不是加索引。
第十一章:索引優化的案例化思路(用框架而非模板)
沒有兩個系統完全相同,但你可以用相同的思考框架。這裡用一個通用案例框架幫你把方法套回自己的需求。
11.1 框架:先定「三個問題」
- 最重要的查詢是什麼?(Top 3 慢與 Top 3 次數)
- 每次查詢有哪些固定條件?哪些是範圍條件?
- 你希望一次拿到的最終資料形態是什麼?(列表欄位 vs 詳情欄位)
AWS實名認證 答案會直接決定主鍵/索引鍵與投影。
11.2 框架:再定「兩個目標」
- 目標一:把慢查詢從 Scan/低效 Query 轉成命中分區的 Query
- 目標二:降低每次請求的回傳資料量(用投影與更精準的排序鍵範圍縮小)
你可以把它當作量化指標:Items 下降、延遲下降、節流下降或成本下降。
第十二章:把優化變成持續流程,而不是一次性工程
索引設計不是上線後就結束。業務需求會變、流量分佈會變、熱門租戶與熱鍵也會變。你需要把索引優化融入運維流程。
12.1 建立查詢指標儀表板
至少追蹤這些:
- 各 API(Query/Scan/Get)延遲分佈(P50/P95/P99)
- table 與各 index 的 throttling 與 consumed capacity
- 平均回傳 Items 數、response size 或行為特徵
當指標偏移,你就知道是容量、索引命中,還是資料量問題。
12.2 用版本化資料模型管理風險
索引新增與鍵重構會影響讀寫路徑。若你需要更改鍵設計,常見做法是採用版本化(例如增加新索引欄位或新鍵並逐步切流)。這樣你可以降低一次性遷移造成的風險。
12.3 對新增 GSI 的影響做成本預估
因為 GSI 會放大寫入更新,你應該提前估算它的成本影響,並在壓測或灰度後驗證。很多索引問題最後不是因為鍵錯,而是成本與寫入延遲影響了整體系統。
結語:速度快慢的本質,是你如何使用資料結構
DynamoDB 的索引優化,重點不在於「猜一個索引」或「加一個欄位就會快」。它是對訪問模式的工程化回應:讓每一次查詢都能精準命中分區,讓範圍縮小發生在資料層,而不是交給應用端用時間換空間。
當你遇到查詢慢,請先確認你是否走了 Query、鍵條件是否能利用分區鍵與排序鍵、投影是否把資料量控制在合理範圍;若仍有延遲或節流,再處理熱點分區與容量。最後,透過持續的指標監控與版本化迭代,讓索引設計跟得上業務成長。
只要你把「查詢慢」拆成可驗證的假設,並用訪問模式倒推鍵設計,DynamoDB 的性能就能從不穩定變成可預期的穩定輸出。

