共计 2212 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点:为什么需要向量数据库?
在 AI 应用爆发的今天,非结构化数据(如图片、音频、文本)处理成为常态。传统关系型数据库在存储和查询这类数据时面临根本性挑战:

- 无法直接计算高维向量(比如 512 维的 BERT 词向量)的相似度
- 全量扫描的暴力搜索方式时间复杂度高达 O(N),当数据量超过百万级时响应延迟不可接受
这就是为什么我们需要专门的向量数据库——它们通过近似最近邻算法(ANN)将搜索复杂度降低到亚线性级别。
技术选型:Chroma vs Qdrant
架构对比
- Chroma
- 轻量级单机设计,采用 SQLite 作为存储后端
- 内置 Embedding 函数,适合快速验证场景
-
典型应用:本地开发、小规模推荐系统原型
-
Qdrant
- 分布式架构,支持水平扩展
- 提供 gRPC 和 REST 双接口
- 典型应用:千万级向量的生产环境
性能基准(测试环境:AWS c5.2xlarge)
| 指标 | Chroma(100 万向量) | Qdrant 集群(3 节点) |
|---|---|---|
| QPS | 1200 | 8500 |
| P99 延迟 | 15ms | 8ms |
| 内存占用 | 2.3GB | 9GB(总和) |
注:测试使用 768 维向量,HNSW 索引,ef=200 参数
实战演示
Chroma 快速入门
import chromadb
# 初始化客户端
client = chromadb.Client()
# 创建集合(类似数据库表)collection = client.create_collection("products")
# 添加带 ID 的向量
collection.add(ids=["id1", "id2"],
embeddings=[[0.1, 0.2, ...], [0.3, 0.4, ...]], # 实际需替换为真实向量
metadatas=[{"category": "book"}, {"category": "electronics"}]
)
# 近似最近邻查询
results = collection.query(query_embeddings=[[0.15, 0.25, ...]], # 查询向量
n_results=2
)
print(results["ids"][0]) # 输出最相似的 ID
Qdrant 集群部署
-
下载 Docker 镜像
docker pull qdrant/qdrant -
启动集群(示例为单节点模式)
docker run -p 6333:6333 qdrant/qdrant -
Python 客户端操作
from qdrant_client import QdrantClient client = QdrantClient(host="localhost", port=6333) # 创建集合 client.create_collection( collection_name="products", vectors_config={"size": 768, "distance": "Cosine"} # 指定向量维度和相似度算法 ) # 批量插入数据 points = [{"id": 1, "vector": [0.1, 0.2, ...], "payload": {"category": "book"}}, {"id": 2, "vector": [0.3, 0.4, ...], "payload": {"category": "electronics"}} ] client.upsert("products", points=points) # 带过滤条件的查询 from qdrant_client.models import Filter results = client.search( collection_name="products", query_vector=[0.15, 0.25, ...], query_filter=Filter(**{"must": [{"key": "category", "match": {"value": "book"}}]}), limit=2 )
生产环境建议
索引选择
- HNSW
- 优点:查询速度快,适合高 QPS 场景
-
缺点:构建耗时较长,内存占用高
-
IVF
- 优点:内存友好,适合静态数据集
- 缺点:需要定期重新训练
维度灾难应对
- 超过 1000 维时考虑:
- 使用 PCA 降维
- 采用二进制量化(Binary Quantization)
- 增加 ef_search 参数(但会降低吞吐)
性能优化技巧
- 批量写入
- Chroma:设置
batch_size=1000 -
Qdrant:使用
parallel=4参数启用并行插入 -
查询优化
# Qdrant 的 filter 预处理技巧 client.search( ..., query_filter=Filter(**{"should": [ # 先评估过滤条件 {"key": "price", "range": {"gte": 100}}, {"key": "stock", "range": {"gt": 0}} ]}) )
安全配置
TLS 加密传输(以 Qdrant 为例)
docker run -p 6333:6333 -p 6334:6334 \
-v $(pwd)/certs:/qdrant/certs \
qdrant/qdrant --config-path /qdrant/config/config.yaml
需提前准备:
– config.yaml 中配置 cert_file/key_file 路径
– 合法的 SSL 证书文件
延伸思考
- 如何实现 ” 价格 <100 元且最相似的 10 个商品 ” 这类混合查询?
- 当标量过滤条件过滤掉 99% 数据时,哪种查询顺序更高效?
- 如何设计 AB 测试来评估不同索引类型的业务影响?
建议读者使用 SIFT 数据集 复现基准测试,欢迎在评论区分享你的实验结果!
正文完
