进阶阅读约 5 分钟

DynamoDB 向量搜索

DynamoDB 在 2026 年 8 月 5 日获得了原生向量搜索。你把嵌入作为项上一个普通的 NumberList 来存储,添加一个向量索引,然后用新的 SearchVectors API 运行近似最近邻查询。

在此之前,相似度搜索意味着把你的表复制到 OpenSearch 或一个单独的向量数据库里,并让两边保持同步。那条管道没了。取而代之的计费模型,和 DynamoDB 里的任何其他东西都不一样。

DynamoDB 支持向量搜索吗?

是的——自 2026 年 8 月 5 日起原生支持。你把嵌入作为项上一个普通的 NumberList 来存储,添加一个向量索引,然后用 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,每次搜索每检查 1 GB 向量数据 $0.002,存储每 GB-月 $0.25(us-east-1)。给嵌入建立索引后,它在基表中的副本恰好按每维度 4 字节计费;同一个列表不建索引时约按 1.9 倍计费。
  • 批量存放仍然归 S3 Vectors:静态存储约便宜 8 倍,批量加载也便宜得多。DynamoDB 赢在毫秒级读取、流式写入,以及向量与它所描述的项住在一起。

向量索引是如何运作的

没有新的属性类型。嵌入就是项上一个普通的数字列表,线上格式为 {"L": [{"N": "0.0132"}, {"N": "-0.0475"}, …]},用你已经在用的同一个 PutItemUpdateItem 写入。

索引是一个单独的结构。DynamoDB 以 32 位浮点精度把向量异步复制进它,连同你投影或过滤的任何属性一起。搜索结果是最终一致的,就像一次 读取。

这个滞后在实践中很小。对我们的实时测试索引,一个刚写入的向量在 PutItem 返回后约 136 ms 就出现在搜索结果里。即便如此,也永远不要在它上面构建读己之写的流程。

异步复制,f32PutItem / UpdateItem基表嵌入为 Number 值的 List向量索引向量 + 过滤属性SearchVectorsTopK + 等值过滤带分数的 Top-K

它在你熟悉的那些索引类型旁边的位置:

向量索引GSILSI
每表上限5205
读取 APISearchVectorsQueryScanQueryScan
PartiQL
容量模式仅按需两者皆可两者皆可
一致性最终一致最终一致可用强一致
建表后添加可以可以不可以

每个索引在创建时固定它的维度数(最多 4,096)和三种距离函数之一。COSINEEUCLIDEAN 的分数越低越相似;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,搜索端点还可能继续拒绝一段时间。没有 waiter;用一次真实搜索在重试循环里探测。当我们把索引和一张空表一起创建时,它在 26 秒内变为 ACTIVE,0.6 秒后就接受了搜索。

搜索把查询嵌入作为一个由 {"N": …} 值组成的裸 JSON 数组接收。不要把它包在一个 DynamoDB L 里。存储的属性用列表类型,请求参数不用,把两者搞混是一个很容易犯的第一个错误:

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,你要求时还有 ConsumedCapacityTopK 上限是 100,没有分页,响应上限是 16 MB。

嵌入本身被排除在结果之外,除非你投影了它并请求它。这个默认值是刻意的;返回向量会同时抬高响应大小和搜索账单。

过滤表达式只接受等值,没有 BETWEENINbegins_with。当索引定义了一个 HASH 属性时,每次搜索都必须为它固定恰好一个值。AWS 对范围运算符的措辞是 "not yet available",所以这一点将来可能放宽。

在第一次部署之前,有两个运维上的意外值得了解。SearchVectors 需要新的 dynamodb:SearchVectors IAM 操作,你现有的任何读取策略里都不包含它。

它还与一个单独的端点通信:search-dynamodb.{region}.amazonaws.com。只覆盖 dynamodb.{region} 的出口允许列表和 VPC 终端节点配置会单单弄坏向量搜索,抛出一个从不说明原因的连接错误。

向量索引如何计费

三个新计费项,全部按字节、每次请求最低 1 KB,叠加在正常的表费用之上(us-east-1,来自 AWS 定价 API,2026-08-15):

计费项StandardStandard-IA
向量写入$0.52/GB$0.65/GB
每次搜索检查的向量数据$0.002/GB$0.0025/GB
存储(表和索引)$0.25/GB-mo$0.10/GB-mo

文档警告说,嵌入在基表中的副本——以 List 内十进制字符串的形式存储——可能比索引里的 f32 副本 "considerably larger"(大得多)。我们在 us-east-1 对实时表测量了写入单位计费,而真相更奇怪。

一个属性上没有向量索引时,其上的嵌入按文档记载的十进制规则计费,约为 f32 大小的 1.9 倍。把一个向量索引指向同一个属性,它的基表计费就降到恰好每维度 4 字节:

维度未建索引的 List 属性(计费)同一属性,建了向量索引(计费)
2561,914 B1,024 B
7685,760 B3,072 B
1,0247,653 B4,096 B
1,53611,501 B6,144 B
3,07222,957 B12,288 B

测量方式是用一个填充属性对写入单位边界做二分搜索,每次写入用全新的项键,校准到字节级。我们完整的 1024 维工单项计 5 个写入单位;同一个项在 embedding 上没有索引时计 8 个。

在同一批测试里,向量写入计费项紧跟 f32 大小。VectorWriteRequestBytes 返回的是每维度 4 字节,在裸索引上外加 11 B 的键开销,在我们的双属性搜索模式下外加 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 ms 到 1 s 的范围内应答,并按整个索引的大小对每次查询计费。

DynamoDB 在毫秒级应答,按搜索检查了什么计费,而不是按索引持有什么。

DynamoDB 向量搜索S3 Vectors
GA2026 年 8 月2025 年 12 月
延迟级别个位数毫秒(AWS 宣称)频繁访问约 100 ms,不频繁访问低于 1 s(AWS 宣称)
规模上限未声明向量数上限;创建索引有 600 GB 表上限(软限制)每索引 20 亿个向量
最大维度4,0964,096
距离函数余弦、欧几里得、点积余弦、欧几里得
索引写入从表异步复制(最终一致)强一致
过滤仅等值,≤18 个属性 + 1 个分区键丰富的元数据过滤,每向量可过滤上限 2 KB
TopK100,无分页10,000,可分页
存储$0.25/GB-mo,存两份(表 + 索引)$0.06/GB-mo,存一份
写入$0.52/GB,每请求最低 1 KB$0.20/GB,每次 PUT 最低 128 KB
查询每检查 1 GB $0.002每百万次请求 $2.50 + 全索引处理字节费

写入最低计费额决定了流式场景的胜负,而它们指的方向和存储费率恰好相反。一次写一个 1024 维向量,每百万次写入的费用(由已验证的费率和我们实测的每项 5 个写入单位 + 4,161 向量写入字节算得):

写入模式DynamoDBS3 Vectors
单向量写入~$5.14/M~$24.41/M
批量(每次 PutVectors 500 个)不适用(写入按项计)~$0.78/M

S3 每次 PUT 128 KB 的最低计费额,让它对人们恰恰以为它便宜的那种工作负载来说成了昂贵选项。把向量一个一个流进 S3 Vectors,你付的费率几乎是 DynamoDB 的 5 倍;批量加载它们,你付的约少 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。廉价地批量加载,静态存储付 $0.06/GB,容忍几百毫秒。
  • 高 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,去浏览你向量索引背后的那些项——嵌入会作为普通的列表属性渲染,就在你用来过滤的字段旁边。

更新于