DynamoDB 向量搜尋
DynamoDB 在 2026 年 8 月 5 日獲得了原生的向量搜尋。你把內嵌向量以普通的 Number 值 List 存在項目上,新增一個向量索引,然後用新的 SearchVectors API 執行近似最近鄰查詢。
在此之前,相似度搜尋意味著把你的資料表複製到 OpenSearch 或一個獨立的向量資料庫,並讓兩邊保持同步。那條管線不再需要了。而取代它的計費模型,在 DynamoDB 裡前所未見。
DynamoDB 支援向量搜尋嗎?
支援——自 2026 年 8 月 5 日起原生支援。你把內嵌向量以普通的 Number 值 List
存放、新增一個向量索引,然後用 SearchVectors API 執行近似最近鄰查詢——
不需要 OpenSearch 副本,也不需要獨立的向量資料庫。它只能用在隨需資料表上,最多
4,096 個維度,並按寫入、搜尋與儲存的位元組數計費。
- 第三個索引家族:向量索引與 和 LSI 並列。一個新的讀取 API(
SearchVectors)、僅支援 ANN、僅限隨需資料表、最多 4,096 個維度。 - 快,而且經過實測:對照我們在 us-east-1 實際建立的 1024 維索引,
SearchVectors回應得和來自同一個用戶端的GetItem一樣快(p50 39 ms 對 44 ms),而一筆剛寫入的向量約 136 ms 後就能被搜尋到。 - 按位元組計費:向量寫入每 GB $0.52、每次搜尋所檢查的向量資料每 GB $0.002、儲存每 GB-月 $0.25(us-east-1)。替內嵌向量建立索引後,它在基礎資料表的副本恰好按每個維度 4 個位元組計費;同一個 list 不建索引則約按 1.9 倍計費。
- S3 Vectors 仍然是大宗儲存的家:靜態儲存約便宜 8 倍,批次載入也便宜得多。DynamoDB 的優勢在毫秒級讀取、串流式寫入,以及讓向量與它所描述的項目住在一起。
向量索引是如何運作的
沒有新的屬性型別。內嵌向量就是項目上一個普通的數字 list,在線路上是 {"L": [{"N": "0.0132"}, {"N": "-0.0475"}, …]},用你已經在用的 PutItem 和 UpdateItem 寫入。
索引是一個獨立的結構。DynamoDB 以 32 位元浮點數的精確度把向量非同步複製進去,連同你投影或用來過濾的任何屬性。搜尋結果是最終一致的,就像一次 讀取。
這個延遲在實務上很小。對照我們實際建立的測試索引,一個剛寫入的向量在 PutItem 返回後約 136 ms 就出現在搜尋結果裡。儘管如此,永遠別把「讀你自己剛寫的」流程建立在它之上。
它與你熟悉的索引類型並排時的位置:
| 向量索引 | GSI | LSI | |
|---|---|---|---|
| 每個資料表上限 | 5 | 20 | 5 |
| 讀取 API | SearchVectors | Query、Scan | Query、Scan |
| PartiQL | 否 | 是 | 是 |
| 容量模式 | 僅限隨需 | 兩者皆可 | 兩者皆可 |
| 一致性 | 最終一致 | 最終一致 | 有強一致可用 |
| 建表後新增 | 是 | 是 | 否 |
每個索引在建立時就固定了維度數(最多 4,096)和三種距離函數之一。COSINE 和 EUCLIDEAN 的分數越低越相似;DOT_PRODUCT 的分數越高越相似,而且可以是負值。這些之後都不能再改。
在你做任何基準測試之前,先說一個精確度上的注意事項。索引以 f32 持有向量;更高精確度的值會被接受,但在進入時會損失精確度。如果你帶著 float64 的內嵌向量而來,每一次距離都是對 f32 副本計算的,所以召回率要對著 f32 去量測,而不是對你的原始向量。
建立一個並對它搜尋
假設你要對支援工單做語意搜尋,讓客服不必匹配關鍵字就能找到「之前也遇過這個問題的客戶」。每個工單項目都帶著一個由它的主旨與內文產生的內嵌向量,用什麼模型產生都行(Titan Text Embeddings V2 在 Bedrock 上每百萬輸入 token 收 $0.02)。
把索引加到現有的資料表上。HASH 元素把每次搜尋限定在一個 product 值內;INLINE_FILTER 屬性(最多 18 個)允許在搜尋時做等值過濾:
aws dynamodb update-table \
--table-name SupportTickets \
--attribute-definitions AttributeName=product,AttributeType=S \
AttributeName=severity,AttributeType=S \
--vector-index-updates '[{"Create": {
"IndexName": "TicketEmbeddings",
"VectorAttribute": {"AttributeName": "embedding"},
"SearchSchema": [
{"AttributeName": "product", "SearchSchemaElementType": "HASH"},
{"AttributeName": "severity", "SearchSchemaElementType": "INLINE_FILTER"}
],
"Projection": {"ProjectionType": "KEYS_ONLY"},
"Dimensions": 1024,
"DistanceFunction": "COSINE"
}}]'這次建置的行為像一次 GSI 回填,但稜角更鋒利。整個建置期間 SearchVectors 都返回 ValidationException,沒有部分結果。
AWS 警告,即使 DescribeTable 已經顯示 ACTIVE,搜尋 endpoint 仍可能繼續拒絕一段時間。沒有 waiter 可用;用一個放在重試迴圈裡的真實搜尋去探測。當我們把索引和一張空資料表一起建立時,它在 26 秒後變為 ACTIVE,再過 0.6 秒就接受了搜尋。
搜尋時,查詢用的內嵌向量以 {"N": …} 值組成的裸 JSON 陣列傳入。別把它包進 DynamoDB 的 L。儲存的屬性用 list 型別,請求參數卻不用,把兩者搞混是最容易犯的第一個錯:
aws dynamodb search-vectors \
--table-name SupportTickets \
--index-name TicketEmbeddings \
--search-vector file://query-embedding.json \
--top-k 5 \
--search-condition-expression "product = :p AND severity = :sev" \
--expression-attribute-values '{":p": {"S": "checkout"}, ":sev": {"S": "high"}}'你會拿回最多 TopK 個項目,按最相似優先排序,每個都帶一個 Score,另外在你要求時附上 ConsumedCapacity。TopK 上限是 100,沒有分頁,回應上限是 16 MB。
內嵌向量本身不會出現在結果裡,除非你投影了它並且要求返回。這個預設是刻意的;返回向量會同時撐大回應和搜尋帳單。
過濾表達式只接受等值,沒有 BETWEEN、IN 或 begins_with。當索引定義了 HASH 屬性時,每次搜尋都必須為它釘死恰好一個值。AWS 對範圍運算子的措辭是 "not yet available",所以這一點日後可能放寬。
有兩個維運上的意外值得在第一次部署前就知道。SearchVectors 需要新的 dynamodb:SearchVectors IAM 動作,而你現有的任何讀取政策都不包含它。
它還會連到一個獨立的 endpoint:search-dynamodb.{region}.amazonaws.com。只涵蓋 dynamodb.{region} 的出站允許清單和 VPC endpoint 設定,會單單弄壞向量搜尋,而那個連線錯誤永遠不會告訴你原因。
向量索引如何計費
三個新的計費項,全都按位元組計費、每次寫入與每次搜尋請求最低 1 KB,並疊加在正常的資料表費用之上(us-east-1,取自 AWS 定價 API,2026-08-15):
| 計費項 | Standard | Standard-IA |
|---|---|---|
| 向量寫入 | $0.52/GB | $0.65/GB |
| 每次搜尋檢查的向量資料 | $0.002/GB | $0.0025/GB |
| 儲存(資料表與索引) | $0.25/GB-月 | $0.10/GB-月 |
文件警告,內嵌向量在基礎資料表的副本——以 List 裡的十進位字串儲存——可能比索引裡的 f32 副本 "considerably larger"。我們在 us-east-1 對實際運行的資料表量測了寫入單位計費,而真相更加離奇。
一個放在沒有向量索引的屬性上的內嵌向量,按文件記載的十進位規則計費,約為 f32 大小的 1.9 倍。把一個向量索引指向同一個屬性,它在基礎資料表的計費就降到恰好每個維度 4 個位元組:
| 維度數 | 未建索引的 List 屬性(計費額) | 同一個屬性、建了向量索引(計費額) |
|---|---|---|
| 256 | 1,914 B | 1,024 B |
| 768 | 5,760 B | 3,072 B |
| 1,024 | 7,653 B | 4,096 B |
| 1,536 | 11,501 B | 6,144 B |
| 3,072 | 22,957 B | 12,288 B |
量測方式是用一個填充屬性對寫入單位的邊界做二分搜尋,每次寫入都用全新的項目鍵,校準到位元組。我們完整的 1024 維工單項目計了 5 個寫入單位;同一個項目若 embedding 上沒有索引則計 8 個。
在同一批量測裡,向量寫入這個計費項緊貼著 f32 大小。VectorWriteRequestBytes 回報為每個維度 4 個位元組,在裸索引上外加 11 B 的鍵負擔,在我們帶兩個屬性的 search schema 上則外加 65 B。
搜尋計費是那個你無法事先算出的計費項。VectorSearchRequestBytes 追蹤 ANN 遍歷檢查了多少向量資料,而 AWS 自己的指引是透過 ReturnConsumedCapacity 去實測,而不是從維度數去估算。
我們的探測給出了第一批資料點。對一個 50 個向量的分割區做 TopK=10,每次搜尋檢查了 22.2-22.4 KB;同一個搜尋在只有 1 個向量的分割區上仍檢查了 21.4 KB,所以在小規模下,每次查詢有一個約 21 KB(約 $0.00000004)的下限。AWS 的教學對它自己的 50 個向量範例回報了 31,449 個位元組。
在定價計算器裡為資料表側的成本建個模;向量計費項會疊加在它已經算出的寫入單位之上。
DynamoDB 向量搜尋與 S3 Vectors 的對比
AWS 現在賣兩種無伺服器的向量儲存,而它們是為相反的存取模式打造的。S3 Vectors(2025 年 12 月 GA)每個索引最多容納 20 億個向量、每 GB-月 $0.06,在 100 毫秒到 1 秒的範圍內回應,且每次查詢都按整個索引的大小計費。
DynamoDB 以毫秒級回應,並按這次搜尋檢查了多少計費,而不是按索引持有多少。
| DynamoDB 向量搜尋 | S3 Vectors | |
|---|---|---|
| GA | 2026 年 8 月 | 2025 年 12 月 |
| 延遲等級 | 個位數毫秒(AWS 宣稱) | 頻繁存取約 100 毫秒、不頻繁存取低於 1 秒(AWS 宣稱) |
| 規模上限 | 未公布向量數上限;建立索引有 600 GB 資料表大小上限(軟性) | 每個索引 20 億個向量 |
| 最大維度數 | 4,096 | 4,096 |
| 距離函數 | 餘弦、歐幾里得、點積 | 餘弦、歐幾里得 |
| 索引寫入 | 從資料表非同步複製(最終一致) | 強一致 |
| 過濾 | 僅等值,≤18 個屬性 + 1 個分割區索引鍵 | 豐富的中繼資料過濾,每個向量可過濾部分上限 2 KB |
| TopK | 100,無分頁 | 10,000,可分頁 |
| 儲存 | $0.25/GB-月,兩份(資料表 + 索引) | $0.06/GB-月,一份 |
| 寫入 | $0.52/GB,每次請求最低 1 KB | $0.20/GB,每次 PUT 最低 128 KB |
| 查詢 | 按檢查量 $0.002/GB | 每百萬次請求 $2.50 + 按整個索引的處理位元組計費 |
寫入的最低計費量決定了串流場景的勝負,而它們指向與儲存費率相反的方向。一次寫一個 1024 維向量、以每百萬次寫入計(由已驗證的費率,加上我們實測的每個項目 5 個寫入單位 + 4,161 個向量寫入位元組算出):
| 寫入模式 | DynamoDB | S3 Vectors |
|---|---|---|
| 逐一寫入單個向量 | 每百萬約 $5.14 | 每百萬約 $24.41 |
批次(每次 PutVectors 500 個) | 不適用(寫入以項目為單位) | 每百萬約 $0.78 |
S3 每次 PUT 最低 128 KB 的計費量,讓它恰恰在人們以為它便宜的那種工作負載上成了昂貴的選項。把向量一個一個串流進 S3 Vectors,你要付近 5 倍於 DynamoDB 的費率;批次載入則便宜約 7 倍。
一個 1024 維語料在每月 100 萬次查詢下的每月儲存與查詢總額,由已驗證的費率算出(寫入成本見上面的每百萬次表格)。S3 Vectors 的查詢費用遵循它公布的公式:整個索引的大小乘以一個分級費率。
DynamoDB 的查詢費用取決於被檢查的位元組數,所以我們給出一個敏感度區間,而不是假裝知道你的遍歷情況:
| 語料規模 | DynamoDB 儲存 | DynamoDB 查詢(檢查 4 / 40 / 400 MB) | S3 Vectors 儲存 | S3 Vectors 查詢 |
|---|---|---|---|---|
| 100 萬個向量 | ~$1.95 | $8 / $80 / $800 | ~$0.23 | ~$11 |
| 1,000 萬個向量 | ~$19.50 | $8 / $80 / $800 | ~$2.35 | ~$80 |
| 1 億個向量 | ~$195 | $8 / $80 / $800 | ~$23.50 | ~$217 |
從那張表能看出兩件事。DynamoDB 的單次查詢成本不隨語料規模成長——一次 ANN 搜尋檢查的是一個鄰域,而不是整個索引,而分割區索引鍵的限定還會讓它更小。
S3 Vectors 的儲存優勢(約 8 倍,因為 DynamoDB 以 4 倍的費率存了兩份 f32 副本)會永遠複利下去,不管有沒有人查詢。
何時該用哪一個
- 向量描述的是你本來就存在 DynamoDB 裡的活項目(工單、商品、使用者工作階段、代理記憶):用向量索引。一條寫入路徑、一個項目、沒有會漂移的同步管線。
- 數百萬個內嵌向量、偶爾才查詢(對文件做 RAG、封存、夜間作業):用 S3 Vectors。批次載入便宜、靜態儲存每 GB $0.06、容忍幾百毫秒。
- 高 QPS 加混合排序(文字相關性 + 向量、分面、彙總):答案仍然是 OpenSearch,它的基礎設施門檻是一個經典無伺服器集合約每月 $350。
- 向量要和關聯式資料連接:Aurora PostgreSQL 加 pgvector,它能縮到零,小型 RAG 工作負載每月不到約 $50。
對一家用 DynamoDB 的團隊來說,誠實的預設是兩個都用。把熱門、需要過濾的向量留在資料表上——那裡的寫入與項目是原子的——再把長尾封存到 S3 Vectors,它強一致的批次寫入使它成為一個乾淨的匯集端。
陷阱
- 無聲的去索引化:一個缺少索引
HASH屬性的項目照樣寫進資料表,卻永遠不會進入向量索引。我們實際重現了它:PutItem成功了,而 15 秒後那個向量在每一個分割區裡都不存在。沒有錯誤、沒有結果,回應裡也沒有任何東西告訴你。 - 維度不對的寫入會被拒絕:不做遷移就切換內嵌模型,每一次寫入都會以一個點名了該屬性與兩邊大小的
ValidationException失敗(Invalid size for parameter,在它自己的頁面上有逐字記錄),因為索引把維度數永遠釘死了。 - 陳舊的內嵌向量:DynamoDB 從不重算向量。改了工單的文字卻不重寫
embedding,搜尋就會悄悄匹配到舊內容。標準修法是 Streams 加一個負責重新產生的消費者。 TopK總是返回 K 個項目:只有三個好結果卻用--top-k 10,你照樣拿到 10 個。用Score判斷相關性,而不是用結果數量,並記得分數的方向會因距離函數而翻轉。- 1 KB 最低計費量:低維度向量不會按比例便宜地計費,寫入和搜尋都一樣。
- 一切都是不可變的:維度數、距離函數,以及
INCLUDE投影的屬性集合,要改都得刪掉重建。索引儲存在索引的整個生命週期裡都計費,不管有沒有人查詢。
在你自己的資料表上試試
向量搜尋繼承了 DynamoDB 其他部分教會你的成本紀律。在決定採用某個內嵌向量之前先量好它的大小,因為項目大小限制仍然適用,而一個 3072 維的內嵌向量會讓每次項目寫入在兩個計費項上都多出 12 KB。
如果你知道 GSI 是如何非同步複製的以及何時該選 GSI 而不是 LSI,索引的機制會讓你感到熟悉。至於對同一批資料做詞彙搜尋,DynamoDB 仍然沒有全文檢索引擎;向量搜尋匹配的是語意,而不是拼寫。
在項目大小計算器裡查一個內嵌向量真實的位元組成本,然後試試 DynoTable,去瀏覽你向量索引背後的那些項目——內嵌向量就以普通 list 屬性的樣子,緊挨著你用來過濾的欄位呈現。