Amazon S3 Vectors (Preview) 实战:云原生向量存储初体验¶
Lab 信息
- 难度: ⭐⭐ 中级
- 预估时间: 45 分钟
- 预估费用: < $1.00(含清理)
- Region: us-east-1
- 最后验证: 2026-03-25
背景¶
2025 年 7 月,AWS 发布了 Amazon S3 Vectors(Preview)—— 首个云原生对象存储中内置向量存储与查询能力的服务。它引入了全新的 Vector Bucket 类型,专为 AI Agent、RAG(检索增强生成)和语义搜索场景设计,号称可将向量数据的上传、存储和查询成本降低最高 90%。
为什么值得关注?
- 传统方案(OpenSearch、Pinecone 等向量数据库)需要单独部署和管理,成本高
- S3 Vectors 无需预置基础设施,按用量付费,继承 S3 的弹性和持久性
- 原生集成 Bedrock Knowledge Bases,一站式构建 RAG 应用
本文将从零开始,通过 AWS CLI 和 Python SDK 完成 S3 Vectors 的完整操作流程,并实测查询性能、元数据过滤、距离度量对比等关键特性。
前置条件¶
- AWS 账号(Preview 期间需在支持 Region 中使用)
- AWS CLI v2(需包含 s3vectors 子命令)
- Python 3 + Boto3(用于 SDK 操作)
- IAM 权限:
s3vectors:*(或按需最小化授权)
核心概念¶
S3 Vectors 引入了三层架构:
| 层级 | 说明 | 类比 |
|---|---|---|
| Vector Bucket | 新的 S3 bucket 类型,专为向量优化 | 数据库实例 |
| Vector Index | bucket 内的向量索引,指定维度和距离度量 | 数据表 |
| Vector | 索引内的单条数据:key + 向量 + 元数据 | 数据行 |
与传统 S3 的关键区别:
| 特性 | 普通 S3 Bucket | Vector Bucket |
|---|---|---|
| 命名空间 | s3 |
s3vectors |
| 存储内容 | 对象(文件) | 向量(embedding + metadata) |
| 查询方式 | Key 精确查找 | 向量相似度搜索 |
| Block Public Access | 可配置 | 强制开启,不可关闭 |
| 创建后可改配置 | 部分可改 | name/加密/维度/距离度量均不可改 |
关键限制¶
| 限制项 | 值 |
|---|---|
| 每 Region 每账号 Vector Bucket 数 | 10,000 |
| 每 Bucket Vector Index 数 | 10,000 |
| 每 Index 最大向量数 | 20 亿 |
| 向量维度范围 | 1 - 4,096 |
| 每向量总 metadata | ≤ 40 KB |
| 每向量可过滤 metadata | ≤ 2 KB |
| PutVectors 每次最多 | 500 条 |
| QueryVectors Top-K 最大 | 100 |
| 写入 + 删除 RPS per index | ≤ 1,000 |
动手实践¶
Step 1: 创建 Vector Bucket¶
# 创建向量专用 bucket
aws s3vectors create-vector-bucket \
--vector-bucket-name my-vectors-demo \
--region us-east-1
验证创建成功:
Step 2: 创建 Vector Index¶
创建使用 Cosine 距离度量的 1024 维向量索引:
aws s3vectors create-index \
--vector-bucket-name my-vectors-demo \
--index-name movies-cosine \
--data-type float32 \
--dimension 1024 \
--distance-metric cosine \
--region us-east-1
不可更改配置
Vector Index 创建后,以下配置不可修改:index name、dimension、distance metric、non-filterable metadata keys。请根据你的 embedding 模型仔细选择。
Step 3: 写入向量数据(Python SDK)¶
import boto3
import random
import math
s3v = boto3.client("s3vectors", region_name="us-east-1")
# 生成归一化的随机向量(实际场景中应使用 embedding 模型)
def gen_vector(dim, seed):
random.seed(seed)
v = [random.gauss(0, 1) for _ in range(dim)]
norm = math.sqrt(sum(x*x for x in v))
return [x/norm for x in v]
# 批量写入向量,附带可过滤元数据
vectors = [
{
"key": "tech-ai",
"data": {"float32": gen_vector(1024, seed=100)},
"metadata": {"category": "technology", "topic": "AI", "year": 2025}
},
{
"key": "tech-cloud",
"data": {"float32": gen_vector(1024, seed=102)},
"metadata": {"category": "technology", "topic": "cloud", "year": 2024}
},
{
"key": "nature-ocean",
"data": {"float32": gen_vector(1024, seed=201)},
"metadata": {"category": "nature", "topic": "ocean", "year": 2023}
},
]
resp = s3v.put_vectors(
vectorBucketName="my-vectors-demo",
indexName="movies-cosine",
vectors=vectors
)
print(f"写入状态: {resp['ResponseMetadata']['HTTPStatusCode']}")
强一致性:写入后立即可查询,无需等待索引构建。
Step 4: 语义相似度查询¶
# 用 tech-ai 的向量作为查询,查找最相似的向量
query_vec = gen_vector(1024, seed=100)
resp = s3v.query_vectors(
vectorBucketName="my-vectors-demo",
indexName="movies-cosine",
queryVector={"float32": query_vec},
topK=5,
returnMetadata=True,
returnDistance=True
)
for v in resp["vectors"]:
print(f"key={v['key']}, distance={v['distance']:.6f}, metadata={v['metadata']}")
实测输出示例:
key=tech-ai, distance=0.000371 # 几乎完全匹配(自身)
key=nature-ocean, distance=0.965107 # 不同主题,距离约 1.0
key=tech-cloud, distance=0.965503 # 不同主题,距离约 1.0
Cosine distance 范围 [0, 2]:0 = 方向完全一致,1 = 正交,2 = 完全相反。
Step 5: 元数据过滤查询¶
S3 Vectors 使用 MongoDB 风格的过滤操作符:
# 只搜索 category=technology 的向量
resp = s3v.query_vectors(
vectorBucketName="my-vectors-demo",
indexName="movies-cosine",
queryVector={"float32": query_vec},
topK=5,
returnMetadata=True,
returnDistance=True,
filter={"category": {"$eq": "technology"}}
)
# 仅返回 tech-ai 和 tech-cloud
# 复合过滤:类别为 nature 且 year >= 2024
resp = s3v.query_vectors(
vectorBucketName="my-vectors-demo",
indexName="movies-cosine",
queryVector={"float32": query_vec},
topK=5,
returnMetadata=True,
returnDistance=True,
filter={"$and": [{"category": {"$eq": "nature"}}, {"year": {"$gte": 2024}}]}
)
支持的过滤操作符:
| 操作符 | 说明 | 示例 |
|---|---|---|
$eq |
精确匹配 | {"genre": {"$eq": "drama"}} |
$ne |
不等于 | {"genre": {"$ne": "comedy"}} |
$gt / $gte |
大于 / 大于等于 | {"year": {"$gte": 2020}} |
$lt / $lte |
小于 / 小于等于 | {"price": {"$lt": 100}} |
$in / $nin |
在/不在数组中 | {"genre": {"$in": ["a", "b"]}} |
$and / $or |
逻辑组合 | {"$and": [{...}, {...}]} |
$exists |
字段是否存在 | {"genre": {"$exists": true}} |
过滤机制
S3 Vectors 在向量搜索过程中同时评估过滤条件(非先搜索再过滤),这意味着更可能找到匹配结果。但当匹配向量很少时,可能返回少于 Top-K 的结果。
测试结果¶
Cosine vs Euclidean 距离对比¶
使用相同数据集,分别在 Cosine 和 Euclidean 索引上查询:
| Key | Cosine Distance | Euclidean Distance | 排名 |
|---|---|---|---|
| tech-ai | 0.000371 | 0.000741 | #1 |
| nature-ocean | 0.965107 | 1.926492 | #2 |
| tech-cloud | 0.965503 | 1.931744 | #3 |
| nature-forest | 1.010939 | 2.019973 | #4 |
| tech-ml | 1.011161 | 2.021582 | #5 |
| nature-mountain | 1.014651 | 2.027812 | #6 |
发现:
- 对于归一化向量,两种度量的排序完全一致
- 数学关系:Euclidean distance ≈ 2 × Cosine distance(对 unit vectors 成立:
euclidean² = 2 × cosine) - 选择建议:使用 embedding 模型推荐的度量即可(Titan Text v2 推荐 Cosine)
查询延迟实测¶
10 次连续查询(Cosine, 1024 维, 6 条数据):
| 指标 | 值 |
|---|---|
| 平均延迟 | 305ms |
| 最小延迟 | 267ms |
| 最大延迟 | 391ms |
- 前 3 次查询偏慢(371-391ms),之后稳定在 267-295ms
- 冷启动效应明显:不频繁访问的 index 首次查询较慢
- 符合官方声明:"sub-second for infrequent queries, as low as 100ms for frequent queries"
维度边界测试¶
| 测试 | 结果 |
|---|---|
| dim=1 创建 + 写入 + 查询 | ✅ 正常工作 |
| dim=4096 创建 + 写入 + 查询 | ✅ 正常,查询延迟 391ms |
dim=1 查询结果验证了 Cosine distance 计算的正确性:
- [0.8] vs [1.0]: distance=0(同方向)
- [0.8] vs [0.5]: distance=0(同方向)
- [0.8] vs [-1.0]: distance=2(完全反方向)
踩坑记录¶
注意
-
Filter 语法是 MongoDB 风格:使用
$eq,$and,$gte等操作符,不是 AWS SDK 风格的andAll/eq。这一点在当前 Boto3 文档中不够显眼,建议直接参考 S3 用户指南的 Metadata filtering 页面。(已查文档确认) -
GetVectors 默认不返回向量数据和 metadata:需要显式传
--return-data和--return-metadata参数。(已查文档确认) -
QueryVectors 默认不返回 distance:需要显式传
returnDistance=True(SDK)或--return-distance(CLI)。(已查文档确认) -
创建后锁死的配置很多:encryption type、dimension、distance metric、non-filterable metadata keys 创建后均不可更改,必须提前规划。(已查文档确认)
费用明细¶
| 资源 | 单价 | 用量 | 费用 |
|---|---|---|---|
| S3 Vectors 存储(Preview) | 按量计费 | 数条测试数据 | < $0.01 |
| S3 Vectors 查询(Preview) | 按量计费 | ~30 次查询 | < $0.01 |
| 合计 | < $0.01 |
清理资源¶
# 1. 删除所有 vector index(需逐个删除)
for idx in movies-cosine; do
aws s3vectors delete-index \
--vector-bucket-name my-vectors-demo \
--index-name $idx \
--region us-east-1
done
# 2. 删除 vector bucket(需先删除所有 index)
aws s3vectors delete-vector-bucket \
--vector-bucket-name my-vectors-demo \
--region us-east-1
# 3. 验证清理完成
aws s3vectors list-vector-buckets --region us-east-1
务必清理
虽然 Preview 期间费用极低,但 Lab 完成后请执行清理步骤,养成良好习惯。
结论与建议¶
S3 Vectors 适合什么场景?
- ✅ 大规模低频查询:百万级向量、每天查询千次级别 —— 成本远低于 OpenSearch
- ✅ RAG 知识库:配合 Bedrock Knowledge Bases,一站式搭建
- ✅ 冷热分层:高频查询用 OpenSearch,低频长尾用 S3 Vectors
- ⚠️ 不适合:毫秒级低延迟(高频场景),复杂的混合搜索(聚合、facet)
与现有方案对比:
| 特性 | S3 Vectors | OpenSearch Serverless | Pinecone |
|---|---|---|---|
| 管理开销 | 零(全托管) | 低 | 低 |
| 查询延迟 | 267ms-1s | 10-100ms | 10-100ms |
| 成本(大规模存储) | 极低 | 高 | 高 |
| 混合搜索 | ❌ 仅向量 | ✅ | 部分 |
| 元数据过滤 | ✅ MongoDB 风格 | ✅ | ✅ |
| AWS 原生集成 | ✅ Bedrock/OpenSearch | ✅ | ❌ |
生产环境建议:
- Preview 阶段不建议用于生产负载
- 提前规划好 dimension 和 distance metric(创建后不可改)
- 元数据设计要区分 filterable 和 non-filterable
- 搭配 Bedrock Knowledge Bases 使用效果最佳