向量数据库几乎是当下 AI 应用的标配——RAG 需要它,语义检索需要它,推荐系统也需要它。但一提到部署,很多人就会犹豫:Milvus 要起一套集群,Qdrant 虽然单节点也能跑,持久化还是绕不开本地磁盘或独立的存储后端。对中小团队来说,"为了几百万条向量搭一套专用存储"这笔账并不好算。
OpenData Vector 走了一条不同的路:把向量索引直接建在对象存储(S3、OSS 等)上,底层依托 SlateDB 作为嵌入式存储引擎,省掉了独立向量存储系统的运维负担。思路简单,但影响不小——下面拆开看。
为什么把向量放在对象存储上有意义
传统向量数据库的架构大致是:计算层 + 专用存储层。计算层负责索引构建和查询,存储层负责向量数据的持久化和召回。这套分工在大规模场景下很合理,但对很多实际业务来说,"专用存储层"本身就是成本大头:
- 需要单独的磁盘规划、副本策略、备份流程
- 存储节点扩缩容和计算节点往往不能独立进行
- 多租户场景下,存储隔离的配置复杂度陡增
对象存储天然具备持久性、可扩展性和低成本,已经是大多数团队的基础设施。如果向量索引能直接跑在对象存储上,就等于复用已有的存储投资,砍掉一整层系统。
这正是 OpenData Vector 的出发点。
核心架构:SlateDB + 对象存储
OpenData Vector 的存储底座是 SlateDB——一个基于对象存储的嵌入式 key-value 数据库(思路类似 RocksDB on S3)。SlateDB 把 LSM-Tree 的写路径搬到对象存储上:WAL 和 SST 文件直接写入 S3 兼容的 bucket,内存中只保留必要的 memtable 和 block cache。
在这个基础上,OpenData Vector 做了几件事:
- 向量索引结构适配对象存储的读写模式——对象存储的读是高延迟、高吞吐的,不适合随机读,但批量 scan 和范围读效率不错。索引设计需要把随机访问尽量转化为顺序访问。
- 写入路径优化——向量插入先攒在内存,批量 flush 到对象存储,减少小文件写入。这与 SlateDB 的 LSM compaction 策略配合。
- 查询路径分层——热数据在本地 cache,冷数据从对象存储拉取,查询延迟取决于 cache 命中率和索引结构的设计。
整体来看,架构非常扁平:没有独立的存储集群,没有额外的 replication 层,对象存储本身就是持久化和冗余的保障。
和传统方案的定位差异
这不是要替代 Milvus 或 Qdrant 的全功能场景。定位差异很明确:
| 维度 | 传统向量数据库 | OpenData Vector |
|---|---|---|
| 存储依赖 | 本地磁盘 / 专用存储集群 | 对象存储(S3/OSS) |
| 部署复杂度 | 中到高 | 低,单进程即可启动 |
| 写入延迟 | 低(本地磁盘) | 中(对象存储 flush 有延迟) |
| 查询延迟 | 低 | 取决于 cache 命中率,冷查询偏高 |
| 扩展方式 | 计算存储分别扩容 | 对象存储天然扩展,计算层按需加 |
| 适用规模 | 千万到亿级向量 | 百万到千万级向量 |
简单说:如果你的向量量级在百万级、对查询延迟容忍在百毫秒级、且已经有对象存储基础设施,OpenData Vector 的性价比会非常突出。反之,如果你需要毫秒级延迟和亿级向量,传统方案仍然更合适。
实践:用 OpenData Vector 搭一个最小向量搜索服务
以下示例基于 OpenData Vector 的公开接口风格构建。由于项目仍在早期,部分 API 可能调整,请以官方仓库最新文档为准——这里展示的是典型使用模式。
1. 准备对象存储配置
假设你有一个 MinIO(本地 S3 兼容)实例在运行:
# config.yaml — OpenData Vector 存储配置
storage:
type: s3
endpoint: "http://localhost:9000"
bucket: "opendata-vector"
access_key: "minioadmin"
secret_key: "minioadmin"
region: "us-east-1"
index:
dimension: 128 # 向量维度
metric: cosine # 距离度量:cosine / l2 / dot
cache_size_mb: 256 # 本地 block cache 大小
flush_batch_size: 1000 # 内存攒多少条向量再写对象存储
2. 写入向量数据
"""向 OpenData Vector 写入向量并执行搜索的最小示例"""
import numpy as np
from opendata_vector import VectorClient # 假设的 SDK 接口
# 连接服务(服务端读取上面的 config.yaml)
client = VectorClient(endpoint="http://localhost:8080")
# 创建 collection
client.create_collection(
name="product_embeddings",
dimension=128,
metric="cosine"
)
# 批量写入向量 + 元数据
vectors = np.random.randn(5000, 128).astype(np.float32)
metadatas = [{"product_id": i, "category": f"cat_{i % 10}"} for i in range(5000)]
client.upsert(
collection="product_embeddings",
vectors=vectors,
ids=list(range(5000)),
metadatas=metadatas
)
print("写入完成,向量已攒批 flush 到对象存储")
3. 执行向量搜索
# 用一条查询向量做 top-K 搜索
query = np.random.randn(128).astype(np.float32)
results = client.search(
collection="product_embeddings",
vector=query,
top_k=10,
filter={"category": "cat_3"} # 可选:元数据过滤
)
for hit in results:
print(f"ID={hit.id}, score={hit.score:.4f}, meta={hit.metadata}")
4. 用 Docker 快速跑一个本地环境
# 启动 MinIO 作为本地对象存储
docker run -d --name minio \
-p 9000:9000 -p 9001:9001 \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
minio/minio server /data --console-address ":9001"
# 创建 bucket
docker exec minio mc alias set local http://localhost:9000 minioadmin minioadmin
docker exec minio mc mb local/opendata-vector
# 启动 OpenData Vector 服务(假设已有官方镜像)
docker run -d --name vector-service \
-p 8080:8080 \
-v $(pwd)/config.yaml:/etc/opendata-vector/config.yaml \
opendata/vector-server:latest
跑完上面几步,你就有一个完整的向量写入→存储→搜索链路,所有持久化数据落在 MinIO 的 bucket 里,没有额外的数据库进程。
冷查询延迟的应对思路
对象存储的读延迟是 OpenData Vector 最明显的短板。几个可以实践的优化方向:
- 调大 block cache:把热向量索引段尽量留在内存,
cache_size_mb根据可用内存上调。 - 预加载热点 collection:服务启动时对高频查询的 collection 做索引预热,把 SST 文件从对象存储拉到本地 cache。
- 读写分离部署:写入节点单进程就够了;查询节点可以多部署几个,共享同一个对象存储 bucket,各自维护本地 cache——这比传统方案的存储副本成本低得多。
- 索引分段策略:如果业务有明显的冷热分区(比如最近 7 天的向量查询频繁),可以在索引层面做分段,热段常驻 cache,冷段按需加载。
什么时候该选它,什么时候不该
适合选 OpenData Vector 的场景:
- 向量量级在百万到千万,不需要毫秒级延迟
- 已有 S3/OSS 基础设施,不想再维护一套存储系统
- 多租户 SaaS,每个租户的向量可以隔离到不同 bucket 或 prefix
- 成本敏感,对象存储的价格远低于专用存储集群
仍然应该用传统向量数据库的场景:
- 实时推荐、在线广告等要求 p99 延迟 < 10ms
- 向量量级过亿,需要分布式索引分片
- 需要丰富的过滤、动态 schema、多模态混合检索等高级功能
- 合规要求数据不能出机房、不能放对象存储
一句话总结:OpenData Vector 不是最快的向量数据库,但可能是运维成本最低的那个。如果你的业务能接受稍高的查询延迟、且对象存储已经在用,它值得认真评估——少维护一整套存储系统,本身就是最大的收益。