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,每次搜索每检查 1 GB 向量数据 $0.002,存储每 GB-月 $0.25(us-east-1)。给嵌入建立索引后,它在基表中的副本恰好按每维度 4 字节计费;同一个列表不建索引时约按 1.9 倍计费。
- 批量存放仍然归 S3 Vectors:静态存储约便宜 8 倍,批量加载也便宜得多。DynamoDB 赢在毫秒级读取、流式写入,以及向量与它所描述的项住在一起。
向量索引是如何运作的
没有新的属性类型。嵌入就是项上一个普通的数字列表,线上格式为 {"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,搜索端点还可能继续拒绝一段时间。没有 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,你要求时还有 ConsumedCapacity。TopK 上限是 100,没有分页,响应上限是 16 MB。
嵌入本身被排除在结果之外,除非你投影了它并请求它。这个默认值是刻意的;返回向量会同时抬高响应大小和搜索账单。
过滤表达式只接受等值,没有 BETWEEN、IN 或 begins_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):
| 计费项 | Standard | Standard-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 属性(计费) | 同一属性,建了向量索引(计费) |
|---|---|---|
| 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 的键开销,在我们的双属性搜索模式下外加 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 | |
|---|---|---|
| GA | 2026 年 8 月 | 2025 年 12 月 |
| 延迟级别 | 个位数毫秒(AWS 宣称) | 频繁访问约 100 ms,不频繁访问低于 1 s(AWS 宣称) |
| 规模上限 | 未声明向量数上限;创建索引有 600 GB 表上限(软限制) | 每索引 20 亿个向量 |
| 最大维度 | 4,096 | 4,096 |
| 距离函数 | 余弦、欧几里得、点积 | 余弦、欧几里得 |
| 索引写入 | 从表异步复制(最终一致) | 强一致 |
| 过滤 | 仅等值,≤18 个属性 + 1 个分区键 | 丰富的元数据过滤,每向量可过滤上限 2 KB |
| TopK | 100,无分页 | 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 向量写入字节算得):
| 写入模式 | DynamoDB | S3 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,去浏览你向量索引背后的那些项——嵌入会作为普通的列表属性渲染,就在你用来过滤的字段旁边。