Amazon OpenSearch Service GPU 加速与自动优化向量索引实战¶
Lab 信息
- 难度: ⭐⭐ 中级
- 预估时间: 60 分钟
- 预估费用: $5-10(含清理)
- Region: us-east-1
- 最后验证: 2026-03-28
背景¶
构建大规模向量数据库是 AI 应用(语义搜索、推荐引擎、RAG、Agent 系统)的核心基础设施之一。传统的向量索引构建面临两大痛点:
- 构建速度慢:十亿级向量索引可能需要数天时间构建 HNSW 图
- 优化难度高:调优 HNSW 参数(ef_construction、m、ef_search)和量化策略需要专家级知识,往往耗时数周
Amazon OpenSearch Service 在 2025 年 12 月推出了两个新功能来解决这些痛点:
- GPU 加速向量索引构建:将 HNSW 图构建卸载到 GPU,速度提升最高 10 倍,成本降至 1/4
- 自动优化(Auto-Optimize):自动评估索引配置,在 30-60 分钟内给出 recall/latency/cost 的最优平衡推荐
本文通过 OpenSearch Serverless 实际验证 GPU 加速功能,对比 GPU 与非 GPU 索引构建的差异。
前置条件¶
- AWS 账号(需要
aoss:*和iam:ListUsers/ListRoles权限) - AWS CLI v2 已配置
- Python 3.8+(安装
opensearch-py和requests-aws4auth)
核心概念¶
GPU 加速 vs 传统索引构建¶
| 对比项 | 传统(CPU) | GPU 加速 |
|---|---|---|
| HNSW 图构建 | CPU 串行 | GPU 并行加速 |
| 构建速度 | 基线 | 最高 10x 提升 |
| 索引构建成本 | 基线 | 约 1/4 |
| 支持引擎 | 所有引擎 | 仅 Faiss |
| 管理 GPU 实例 | N/A | 无需管理,自动分配和释放 |
| 计费模型 | OCU-hour | OCU - Vector Acceleration(秒级粒度) |
| 搜索质量 | 基线 | 完全一致 |
Auto-Optimize 工作流¶
- 准备 parquet 格式向量数据集 → 上传到 S3
- 提交 auto-optimize job,指定可接受的 recall 和 latency 阈值
- 30-60 分钟后获得推荐配置(HNSW 参数、量化方法等)
- 可选:使用推荐自动创建索引并灌入数据
关键限制¶
- GPU 加速:仅支持 Faiss 引擎(不支持 PQ、IVF、NMSLIB、Lucene)
- GPU 加速:需要 OpenSearch 3.1+ 或 Serverless Vector Collection
- GPU 加速:5 个 Region(us-east-1, us-west-2, ap-southeast-2, ap-northeast-1, eu-west-1)
- Auto-Optimize:9 个 Region
- 最佳效果:shard 至少 100 万文档
动手实践¶
Step 1: 创建 Serverless 前置策略¶
OpenSearch Serverless 需要三个策略:加密策略、网络策略、数据访问策略。
加密策略(使用 AWS 托管密钥):
cat > /tmp/enc-policy.json << 'EOF'
{
"Rules": [
{
"ResourceType": "collection",
"Resource": ["collection/gpu-vector-test"]
}
],
"AWSOwnedKey": true
}
EOF
aws opensearchserverless create-security-policy \
--name gpu-vector-test-enc \
--type encryption \
--policy file:///tmp/enc-policy.json \
--region us-east-1
网络策略(公开访问 — 用于测试):
cat > /tmp/net-policy.json << 'EOF'
[
{
"Rules": [
{
"ResourceType": "collection",
"Resource": ["collection/gpu-vector-test"]
},
{
"ResourceType": "dashboard",
"Resource": ["collection/gpu-vector-test"]
}
],
"AllowFromPublic": true
}
]
EOF
aws opensearchserverless create-security-policy \
--name gpu-vector-test-net \
--type network \
--policy file:///tmp/net-policy.json \
--region us-east-1
数据访问策略(替换 ACCOUNT_ID 和 USER_NAME):
cat > /tmp/dap-policy.json << 'EOF'
[
{
"Rules": [
{
"ResourceType": "collection",
"Resource": ["collection/gpu-vector-test"],
"Permission": [
"aoss:CreateCollectionItems",
"aoss:DeleteCollectionItems",
"aoss:UpdateCollectionItems",
"aoss:DescribeCollectionItems"
]
},
{
"ResourceType": "index",
"Resource": ["index/gpu-vector-test/*"],
"Permission": [
"aoss:CreateIndex",
"aoss:DeleteIndex",
"aoss:UpdateIndex",
"aoss:DescribeIndex",
"aoss:ReadDocument",
"aoss:WriteDocument"
]
}
],
"Principal": ["arn:aws:iam::ACCOUNT_ID:user/USER_NAME"]
}
]
EOF
aws opensearchserverless create-access-policy \
--name gpu-vector-test-dap \
--type data \
--policy file:///tmp/dap-policy.json \
--region us-east-1
Step 2: 创建 GPU 加速的 Serverless Vector Collection¶
aws opensearchserverless create-collection \
--name gpu-vector-test \
--type VECTORSEARCH \
--description "GPU acceleration vector index test" \
--vector-options '{"ServerlessVectorAcceleration": "ENABLED"}' \
--region us-east-1
等待 Collection 变为 ACTIVE(约 5-6 分钟):
# 检查状态
aws opensearchserverless batch-get-collection \
--ids <COLLECTION_ID> \
--region us-east-1 \
--query "collectionDetails[0].[status,collectionEndpoint]" \
--output text
输出示例:ACTIVE https://<id>.us-east-1.aoss.amazonaws.com
Step 3: 创建向量索引(GPU vs 非 GPU)¶
使用 Python 的 opensearch-py 库连接 Serverless Collection:
import json
import boto3
from opensearchpy import OpenSearch, RequestsHttpConnection
from requests_aws4auth import AWS4Auth
REGION = "us-east-1"
ENDPOINT = "<your-collection-id>.us-east-1.aoss.amazonaws.com"
session = boto3.Session(region_name=REGION)
creds = session.get_credentials()
awsauth = AWS4Auth(
creds.access_key, creds.secret_key, REGION, "aoss",
session_token=creds.token
)
client = OpenSearch(
hosts=[{"host": ENDPOINT, "port": 443}],
http_auth=awsauth, use_ssl=True, verify_certs=True,
connection_class=RequestsHttpConnection, timeout=300
)
创建 GPU 加速索引(remote_index_build.enabled: true):
gpu_index_body = {
"settings": {
"index.knn": True,
"index.knn.remote_index_build.enabled": True # 启用 GPU 加速
},
"mappings": {
"properties": {
"vector_field": {
"type": "knn_vector",
"dimension": 768,
"method": {
"name": "hnsw",
"space_type": "l2",
"engine": "faiss"
}
},
"text": {"type": "text"},
"category": {"type": "keyword"}
}
}
}
client.indices.create(index="gpu-vector-idx", body=gpu_index_body)
创建非 GPU 索引(对照组):
nongpu_index_body = {
"settings": {
"index.knn": True,
"index.knn.remote_index_build.enabled": False # 禁用 GPU 加速
},
"mappings": {
"properties": {
"vector_field": {
"type": "knn_vector",
"dimension": 768,
"method": {
"name": "hnsw",
"space_type": "l2",
"engine": "faiss"
}
},
"text": {"type": "text"},
"category": {"type": "keyword"}
}
}
}
client.indices.create(index="nongpu-vector-idx", body=nongpu_index_body)
Serverless 注意事项
在 Serverless Collection 上,必须显式设置 index.knn.remote_index_build.enabled。
Domain 启用 GPU 后此设置默认为 true,但 Serverless 不会自动继承。
Step 4: 灌入向量数据¶
生成 50,000 个 768 维归一化向量(模拟 text embedding):
import numpy as np
import time
def bulk_ingest(client, index_name, num_vectors=50000, dim=768, batch_size=500):
np.random.seed(42)
start_time = time.time()
total_docs = 0
for batch_start in range(0, num_vectors, batch_size):
batch_end = min(batch_start + batch_size, num_vectors)
batch_count = batch_end - batch_start
vecs = np.random.randn(batch_count, dim).astype(float)
norms = np.linalg.norm(vecs, axis=1, keepdims=True)
vecs = vecs / norms
# Serverless 不支持自定义 doc ID,必须省略 _id 字段
bulk_body = ""
for j in range(batch_count):
bulk_body += json.dumps({"index": {"_index": index_name}}) + "\n"
bulk_body += json.dumps({
"vector_field": vecs[j].tolist(),
"text": "Document %d" % (batch_start + j),
"category": "cat_%d" % ((batch_start + j) % 10)
}) + "\n"
resp = client.bulk(body=bulk_body)
if not resp.get("errors"):
total_docs += batch_count
elapsed = time.time() - start_time
print("%s: %d docs in %.1fs (%.0f docs/s)" % (
index_name, total_docs, elapsed, total_docs / elapsed))
return total_docs, elapsed
# 分别灌入 GPU 和非 GPU 索引
bulk_ingest(client, "gpu-vector-idx")
bulk_ingest(client, "nongpu-vector-idx")
Serverless 不支持自定义 Document ID
与 Managed Domain 不同,Serverless Collection 的 _bulk 和 _doc API 不支持指定 _id。
如果 bulk 请求中包含 _id,会返回 illegal_argument_exception: Document ID is not supported。
这是实测发现的行为,官方文档未明确说明。
Step 5: kNN 搜索验证¶
query_vec = np.random.randn(768).astype(float).tolist()
search_body = {
"size": 5,
"query": {
"knn": {
"vector_field": {
"vector": query_vec,
"k": 5
}
}
}
}
# 对比两个索引的搜索结果
for idx in ["gpu-vector-idx", "nongpu-vector-idx"]:
resp = client.search(index=idx, body=search_body)
print("\n%s:" % idx)
for hit in resp["hits"]["hits"]:
print(" score=%.4f text=%s" % (hit["_score"], hit["_source"]["text"]))
测试结果¶
数据灌入吞吐量¶
| 索引 | GPU 加速 | 文档数 | 灌入时间 | 吞吐量 |
|---|---|---|---|---|
| gpu-vector-idx | ✅ 启用 | 50,000 | 208.6s | 240 docs/s |
| nongpu-vector-idx | ❌ 禁用 | 50,000 | 216.8s | 231 docs/s |
| gpu-small-idx | ✅ 启用 | 1,000 | 7.9s | 127 docs/s |
分析:灌入吞吐量差异约 4%。这是符合预期的——GPU 加速主要作用于 HNSW 图的构建阶段(segment merge),而非原始数据的 ingestion 吞吐。AWS 官方 benchmark 在十亿级规模时观察到 6.4x - 13.8x 的加速,我们 50K 规模的测试数据量较小,刚过默认触发阈值(50 MB)。
kNN 搜索质量对比¶
| 排名 | GPU 索引结果 | 非 GPU 索引结果 | Score |
|---|---|---|---|
| 1 | Document 47507 | Document 47507 | 0.0013 |
| 2 | Document 27948 | Document 27948 | 0.0013 |
| 3 | Document 38153 | Document 38153 | 0.0013 |
| 4 | Document 27749 | Document 27749 | 0.0013 |
关键发现:GPU 和非 GPU 索引返回完全相同的 Top-4 最近邻结果,score 一致。这证明 GPU 加速不会影响搜索质量(recall),仅加速索引构建过程。
搜索延迟(10 次查询)¶
| 索引 | Avg | P50 | P90 | P99 |
|---|---|---|---|---|
| gpu-vector-idx | 763.8ms | 693.5ms | 1273.6ms | 1273.6ms |
| nongpu-vector-idx | 540.8ms | 680.2ms | 713.3ms | 713.3ms |
说明:搜索延迟差异属于 Serverless 正常波动范围(冷启动、OCU 分配等),与 GPU 加速无关。GPU 加速仅影响索引构建阶段。
踩坑记录¶
Serverless 特有限制
-
不支持
approximate_threshold设置:尝试设置index.knn.advanced.approximate_threshold会返回 "unknown setting" 错误。此设置仅在 Managed Domain 上可用。(已查文档确认) -
不支持自定义 Document ID:Serverless 的 bulk/index API 不接受
_id参数,返回 "Document ID is not supported" 错误。(实测发现,官方未明确记录) -
不支持 Force Merge API:
_forcemerge返回 404。Serverless 由服务自动管理 segment merge。(实测发现,Serverless API 限制) -
不支持 Index Stats API:
_stats返回 404。只能通过_countAPI 获取文档数量。(实测发现,Serverless API 限制) -
Collection 必须显式设置
remote_index_build:与 Domain 不同,Serverless Collection 即使启用了 GPU 加速,索引级别的index.knn.remote_index_build.enabled不会自动设为 true,必须在创建索引时显式指定。(已查文档确认)
费用明细¶
| 资源 | 单价 | 用量 | 费用 |
|---|---|---|---|
| Serverless Collection (min 4 OCU) | ~$0.24/OCU-hr | ~1.5 hr | ~$1.44 |
| GPU Acceleration OCU | 按秒计费 | 测试期间 | ~$0.50 |
| 合计 | ~$2-5 |
成本控制提示
Serverless Collection 创建后即使没有数据也会消耗最低 4 OCU(2 indexing + 2 search)。 测试完成后务必立即删除 Collection。
清理资源¶
# 1. 删除 Collection(索引会随之删除)
aws opensearchserverless delete-collection \
--id <COLLECTION_ID> \
--region us-east-1
# 2. 等待 Collection 删除完成
aws opensearchserverless batch-get-collection \
--ids <COLLECTION_ID> \
--region us-east-1
# 3. 删除安全策略
aws opensearchserverless delete-security-policy \
--name gpu-vector-test-enc \
--type encryption \
--region us-east-1
aws opensearchserverless delete-security-policy \
--name gpu-vector-test-net \
--type network \
--region us-east-1
# 4. 删除数据访问策略
aws opensearchserverless delete-access-policy \
--name gpu-vector-test-dap \
--type data \
--region us-east-1
务必清理
Lab 完成后请执行清理步骤。Serverless Collection 即使空闲也会产生最低 OCU 费用(~$0.96/hr for 4 OCU)。
结论与建议¶
适用场景¶
- 大规模向量数据库构建:十亿级向量索引构建从数天缩短到 1 小时内
- AI 应用快速迭代:RAG、语义搜索、推荐系统的向量索引需要频繁重建
- 成本敏感场景:GPU 加速不仅更快,索引构建成本也降至 1/4
使用建议¶
- 数据规模要够大:shard 至少 100 万文档才能充分发挥 GPU 加速优势。50K 量级差异不明显
- 选择 Faiss 引擎:GPU 加速仅支持 Faiss(HNSW),不支持 Lucene 或 NMSLIB
- Serverless vs Domain:
- Serverless 更适合快速实验和按需使用
- Domain(OpenSearch 3.1+)适合需要 Force Merge、自定义 Doc ID 等高级功能的生产场景
- 结合 Auto-Optimize:先用 Auto-Optimize 找到最优参数配置,再用 GPU 加速构建大规模索引
GPU 加速 + Auto-Optimize 组合使用¶
这个组合解决了向量数据库的两大痛点:不知道怎么配 + 配好了构建太慢。