入門閱讀時間 3 分鐘

DynamoDB 中的 Query 與 Scan

Query 依 partition key 讀取單一的項目集合(可選擇性地以 sort key 縮小範圍);Scan 讀取整個表格並在之後篩選。它們在 API 中看起來相似,但計費 — 以及擴展方式 — 完全不同。

在 DynamoDB 中什麼時候應該使用 Query 而非 Scan?

只要你能指定所需的 partition,就使用 Query——它只讀取一個項目集合,且僅對相符的項目計費。只有在一次性匯出或極小的表格時才使用 Scan;它會讀取每一個項目,並在任何 FilterExpression 執行之前對整個表格計費。面對真實資料,Query 是更好的選擇。

  • Query 是針對性的:你只為相符 partition 中的項目付費。
  • Scan 是窮舉性的:你為讀取每個項目付費,然後用一個在讀取被計費之後才執行的 FilterExpression 把大部分丟掉。

在任何具一定規模的表格上,帶 filter 的 Scan 正是那個經典的「為什麼我的帳單爆表、延遲還比 RDS 差」的陷阱。

並排比較

QueryScan
讀取一個 partition(依 PK)表格中的每一個項目
計費容量partition 中相符的項目整個表格,在篩選之前
FilterExpression在讀取後套用 — 仍為讀取計費相同 — 篩選永遠不會降低成本
延遲隨表格成長維持平穩隨表格大小成長
分頁1 MB/頁 → LastEvaluatedKey1 MB/頁;可平行化
適用於已知的存取模式一次性匯出、極小的設定表格

關鍵陷阱:在這兩種操作上,FilterExpression 都在 DynamoDB 計費讀取之後才執行。一個「傳回 10 列」的 Scan 可能為讀取一百萬列計費 — 篩選是一種便利,絕不是成本控制。

一次完整 Scan 實際的成本

用數字說話。DynamoDB 以 4 KB 為單位計量讀取:一次讀取每 4 KB 花費一個,一次讀取則只要一半。QueryScan 加總它們觸及的每一個項目的大小 — 而不是它們傳回的每一個項目 — 並向上進位到下一個 4 KB。

以一個有 100 萬個項目、平均每項 2 KB(約 2 GB 資料)的表格為例,而某個存取模式只需要其中 10 個項目:

讀取的項目計量的資料讀取單位(最終一致)
Scan + FilterExpression1,000,000~2 GB~262,000
以相符鍵執行的 Query1020 KB3

同樣的 10 個項目,成本相差五個數量級 — 而且不論篩選命中十個項目還是零個,那個 ~262,000 每次執行 Scan 都會照樣計費。在計費下,那些是直接算進帳單的請求單位;在的表格上,一次大型 Scan 會與正式環境的流量競爭輸送量,並可能把它節流成 ProvisionedThroughputExceededException

還有三個讓人吃驚的成本事實:

  • Select: COUNT 不是免費的。 一次計數的 Query 或 Scan 消耗的讀取容量與實際讀取那些項目完全相同 — 只是不把它們傳回。
  • Limit 限制的是被評估的項目數,不是相符的項目數。 搭配篩選時,一頁可能空手而回,卻仍為滿滿一頁的讀取計費。
  • 你永遠不必用猜的。 傳入 ReturnConsumedCapacity: TOTAL,每個回應都會回報它剛剛消耗的容量。

項目大小計算器檢查你自己的項目有多重,再用定價計算器把讀取單位換算成每月帳單。

使用 Query

Query  PK = "USER#42"  AND  SK begins_with "ORDER#"

如果你發現自己為了回答一個常見的存取模式而動手用 Scan,那是個建模訊號:新增一個 Global Secondary Index,讓該模式變成一個 Query

抉擇歸結到一個問題 — 你能說出你需要哪個 partition 嗎?

YesNoYesNoAccess patternPartition key known?Query reads one partitionCan a GSI key it?Add a GSIScan reads the whole table

鍵已知就 Query;若否,加個 GSI 讓它變成已知,只有在沒有任何鍵適用時才退而用 Scan

Scan 何時沒問題

一次性匯出、極小的設定表格,以及刻意分頁掃過整個表格的背景工作。當你真的必須讀取所有內容時,使用 Segment/TotalSegments 將一個 Scan 分散到多個 worker(一次 — 見 DynamoDB 平行掃描),並用 LastEvaluatedKey 好好地分頁(分頁指南)。如果問題出在一個你已經在跑的 Scan,為什麼 Scan 又慢又貴會帶你走一遍分診。

對 DynamoDB 反射性地下 SELECT * FROM table 是同一種反模式換上 PartiQL 的外衣 — 它會編譯成一個 Scan。當你真的需要跨項目的分析(一個 GROUP BY、一個 JOIN、一個聚合)時,DynoTable 的 SQL Workbench 會在一個有界的結果集上於用戶端執行它們,而非猛敲表格。

試用 DynoTable,對你自己的表格執行並檢視這些查詢 — 它會顯示它執行的每一次操作所消耗的容量。

已更新