共计 1843 个字符,预计需要花费 5 分钟才能阅读完成。
为什么需要向量数据库?
传统关系型数据库在处理高维向量数据时面临三大挑战:
- 查询效率低下 :用
WHERE语句计算欧氏距离需全表扫描,100 万条 768 维向量的暴力搜索需要约 3 秒 - 存储空间浪费:用 BLOB 类型存储向量时,无法利用向量的几何特性压缩,1 亿条向量可能占用 TB 级空间
- 功能支持缺失:缺乏专门的相似度搜索语法(如
WHERE vector ~= [0.1,0.2,...])
近似最近邻搜索(ANN/Approximate Nearest Neighbor)通过牺牲少量精度换取百倍速度提升,典型场景包括:
- 推荐系统:” 查找与用户 A 兴趣相似的 Top100 商品 ”
- 图像检索:” 找出与这张猫图最像的 5 张图片 ”
- NLP 语义搜索:” 返回与 ’ 如何理财 ’ 最相关的问答 ”
主流方案技术对比
| 特性 | Faiss (Meta) | Milvus (Zilliz) | Weaviate |
|---|---|---|---|
| 核心架构 | 单机库 | 分布式系统 | 服务 + 内置向量模块 |
| 索引类型 | IVF_PQ, HNSW | IVF_SQ8, HNSW | HNSW |
| 语言支持 | Python/C++ | 多语言 SDK | GraphQL API |
| GPU 加速 | 支持 | 企业版支持 | 不支持 |
| 数据持久化 | 需自行处理 | 内置 | 内置 |
| 典型延迟(1M) | 2ms (IVF_PQ) | 5ms (IVF_SQ8) | 15ms (HNSW) |
HNSW 算法原理解析

- 多层结构:最底层包含全部数据点,上层随机抽样形成 ” 高速公路 ”
- 贪婪搜索:从顶层开始,每层找到局部最近邻后进入下层细化
- 双向链接:每个节点维护多个邻居指针,平衡连通性与内存开销
Faiss 实战代码示例
import faiss
import numpy as np
# 生成随机数据
d = 768 # 向量维度
nb = 100000 # 数据库大小
nq = 10 # 查询数量
np.random.seed(1234)
xb = np.random.random((nb, d)).astype('float32')
xq = np.random.random((nq, d)).astype('float32')
# 构建 IVF_PQ 索引
nlist = 100 # 聚类中心数
m = 8 # 子量化器数量
quantizer = faiss.IndexFlatL2(d)
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 8)
# 训练并添加数据
assert not index.is_trained
index.train(xb)
assert index.is_trained
index.add(xb)
# GPU 加速
res = faiss.StandardGpuResources()
gpu_index = faiss.index_cpu_to_gpu(res, 0, index)
# 查询处理
k = 5 # 返回最近邻数
D, I = gpu_index.search(xq, k)
print("查询结果索引:", I)
print("距离:", D)
# 资源清理
del gpu_index
faiss.downcast_index(index).make_direct_map() # 释放直接映射
性能优化策略
内存 - 召回率权衡
| 配置组合 | 内存占用 | 召回率 @10 |
|---|---|---|
| IVF_PQ(nlist=100) | 0.5GB | 85% |
| IVF_SQ8(nlist=500) | 1.2GB | 92% |
| HNSW(M=32) | 3.8GB | 98% |
Milvus 分片建议
- 按数据特征分片:用户画像 / 商品特征分离部署
- 冷热分离:频繁更新的向量单独分片
- 资源预留:每个数据节点预留 20% 内存缓冲
生产环境避坑指南
- 向量归一化:余弦相似度搜索前必须做 L2 归一化
faiss.normalize_L2(xb) # 入库前处理 - OOM 排查步骤:
- 检查
top确认不是其他进程占用 - 用
faiss.get_mem_usage_kb()监控索引内存 - 降低
nprobe参数减少搜索范围
延伸思考
当处理 1000+ 维向量时:
– 对于 低内存 场景:PQ(Product Quantization)将向量切分后分段量化
– 对于 高精度 需求:OPQ(Oriented Product Quantization)先旋转向量再量化
– 对于 混合查询 :SCANN(An Efficient Vector Search) 支持同时处理属性过滤
建议通过实际业务数据测试不同算法:
# 评估不同量化方法
results = {}
for method in ['PQ', 'OPQ', 'SQ']:
index = create_index(method, d=1024)
t, recall = benchmark(index, xq, k)
results[method] = (t, recall)
最终选择应权衡:搜索延迟、内存占用、召回率三个关键指标。
正文完
发表至: 未分类
近两天内
