向量数据库工程实践:从HNSW索引到混合检索架构
为什么需要向量数据库
随着大模型时代的到来,文本、图像、音频等数据被编码为高维向量(Embedding),传统数据库无法高效处理"语义相似度搜索"。向量数据库应运而生,专门优化了最近邻搜索(ANN)场景,达到亚秒级的十亿级向量检索。
向量索引算法全景
ANN算法的核心思想是:放弃精确解的保证,换取搜索效率的指数级提升。
| 算法 | 原理 | 构建时间 | 搜索时间 | 内存占用 | 召回率 |
|---|---|---|---|---|---|
| Flat Brute-force | 暴力扫描所有向量 | O(1) | O(Nd) | O(Nd) | 1.0 |
| IVF(倒排文件) | 先聚类再倒排检索 | O(NK) | O(√N·d) | O(Nd) | 0.9~0.99 |
| HNSW(层级图) | 多层跳跃图搜索 | O(NlogN) | O(logN·d) | O(Nd·M) | 0.95~0.999 |
| DiskANN | Vamana图+磁盘页 | O(NlogN) | O(logN·d·IO) | O(N·d/压缩率) | 0.9~0.97 |
HNSW索引详解
HNSW(Hierarchical Navigable Small World)是当前工程上最成功的ANN索引,被Milvus、Qdrant、pgvector、Elasticsearch等广泛采用。
层级结构:
- 第0层包含所有节点
- 上层节点按指数衰减概率分布
- 高层用于快速"跳远",低层用于精细搜索
# HNSW搜索伪代码
import heapq
def hnsw_search(graph, query, top_k, ef_search):
# 从最高层的入口点开始
entry_point = graph.top_level_entry
current_level = graph.max_level
# 逐层贪心搜索,直到第0层
while current_level > 0:
entry_point = greedy_search_layer(query, entry_point, current_level, ef=1)
current_level -= 1
# 在第0层做beam search
candidates = beam_search_layer(query, entry_point, current_level, ef=ef_search)
return heapq.nlargest(top_k, candidates, key=lambda x: x.distance)
def greedy_search_layer(query, entry, level, ef):
visited = {entry}
candidates = [DistanceNode(query.distance(entry), entry)]
best = candidates[0]
while candidates:
current = heapq.heappop(candidates)
if current.distance > best.distance:
break
for neighbor in graph.neighbors(current.node, level):
if neighbor not in visited:
visited.add(neighbor)
dist = query.distance(neighbor)
if dist < best xss=removed>
关键参数:
- M:每个节点的最大连接数(通常16~64),影响图连通性
- ef_construction:构建时的beam width,越大图质量越高但构建越慢
- ef_search:搜索时的beam width,与召回率正相关,与延迟负相关
PostgreSQL + pgvector的工程实践
对于已有PostgreSQL基础设施的团队,pgvector是最低成本的向量搜索方案。
-- 启用扩展
CREATE EXTENSION vector;
-- 创建向量表
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536) -- OpenAI ada-002 维度
);
-- 创建HNSW索引(构建参数可调)
CREATE INDEX ON documents
USING hnsw (embedding vector_l2_ops)
WITH (m = 16, ef_construction = 200);
-- 语义搜索查询(L2距离)
SELECT id, content, embedding <-> '[0.1, 0.2, ...]' AS distance
FROM documents
ORDER BY distance ASC
LIMIT 10;
-- 混合检索:向量相似度 + BM25全文搜索的RRF融合
WITH vec_search AS (
SELECT id, embedding <-> $1 AS vec_score
FROM documents ORDER BY vec_score LIMIT 20
),
text_search AS (
SELECT id, ts_rank(to_tsvector(content), $2) AS text_score
FROM documents
WHERE to_tsvector(content) @@ $2 ORDER BY text_score DESC LIMIT 20
),
rrf AS (
SELECT id,
1.0 / (60 + ROW_NUMBER() OVER (PARTITION BY id ORDER BY vec_score)) +
1.0 / (60 + ROW_NUMBER() OVER (PARTITION BY id ORDER BY text_score)) AS rrf_score
FROM vec_search FULL OUTER JOIN text_search USING (id)
)
SELECT d.id, d.content, r.rrf_score
FROM rrf r JOIN documents d ON r.id = d.id
ORDER BY r.rrf_score DESC LIMIT 10;
生产环境优化策略
1. 量化压缩
乘积量化(Product Quantization)将原始向量压缩到1/8~1/16大小,以轻微精度损失换取内存和IO节省。Milvus的IVF_PQ索引就是典型应用。
2. 分区与分片
按租户ID或数据类别对向量做分区(Partition),搜索时只扫描相关分区。Milvus的Partition Key机制能自动路由查询。
3. 存算分离
Milvus 2.4+引入的Streaming Node架构将存储与计算解耦:日志存于对象存储(S3),计算层只负责向量检索,存储成本下降80%。
4. 混合检索
单纯的向量搜索无法处理精确匹配条件(如"价格=100元")。生产系统通常采用向量+标量过滤的混合方案:先通过B+Tree索引过滤metadata,再对候选集做ANN搜索。
典型应用场景
- RAG(检索增强生成):将文档按chunk编码存入向量库,用户问题也做Embedding,召回top-k个最相关chunk作为LLM上下文
- 图像搜索:CLIP/ViT编码后存入向量库,以图搜图或跨模态检索
- 推荐系统:用户行为序列的Embedding表示,基于相似用户或物品做推荐
- 异常检测:将时序数据窗口编码为向量,检测与历史模式的偏离程度
向量数据库的发展仍在快速推进,DiskANN等磁盘索引方案让十亿级检索无需全量加载内存,而向量+标量的混合查询、多模态联合检索等能力正在成熟。对于生产环境,选择pgvector(已有PG生态)、Milvus(独立部署)还是Pinecone(全托管),取决于数据规模与运维成本的权衡。

发表评论 取消回复