跳转至

12-向量数据库

向量数据库

面向 AI 时代的新一代数据库技术:向量相似性搜索与检索增强生成(RAG)


1. 向量数据库概述

1.1 什么是向量数据库

向量数据库是专门用于存储、索引和查询高维向量(Embedding)的数据库系统。与传统关系型数据库通过精确匹配进行查询不同,向量数据库通过计算向量之间的相似度来检索最相关的结果。

核心能力: - 高维向量存储(通常 128-4096 维) - 近似最近邻搜索(ANN) - 毫秒级检索延迟 - 支持十亿级向量规模 - 元数据过滤 + 向量搜索的混合查询

1.2 典型应用场景

场景 说明
RAG(检索增强生成) 为 LLM 提供外部知识检索
语义搜索 基于含义而非关键词的搜索
推荐系统 基于内容或用户向量的相似推荐
图像检索 以图搜图、视觉相似性匹配
异常检测 识别与正常模式偏离的向量
去重 基于语义的近似重复检测

1.3 主流向量数据库对比

特性 Milvus Pinecone Chroma Qdrant Weaviate
开源 ✅ Apache 2.0 ❌ 商业 SaaS ✅ Apache 2.0 ✅ Apache 2.0 ✅ BSD-3
部署方式 自托管 / Zilliz Cloud 全托管 嵌入式 / 服务端 自托管 / Cloud 自托管 / Cloud
最大规模 十亿级 十亿级 百万级 十亿级 十亿级
语言 SDK Python/Java/Go/Node Python/Node/Go Python/JS Python/Rust/Go Python/JS/Go/Java
混合搜索 ✅ 基础
标量过滤 ✅ 高级 ✅ 基础 ✅ 高级 ✅ 高级
多租户
GPU 加速 N/A
适用场景 大规模生产环境 快速上手、无运维 原型/小项目 中大规模、Rust 高性能 GraphQL 原生、多模态

1.4 选型建议

Text Only
快速原型 / 本地开发 → Chroma(嵌入式,零配置)
中小规模生产 → Qdrant(性能好,资源占用低)
大规模生产 → Milvus(成熟稳定,功能全面)
无运维需求 → Pinecone(全托管 SaaS)
GraphQL / 多模态 → Weaviate(对象存储 + 向量)

2. 向量索引算法

2.1 暴力搜索(Flat / Brute-Force)

最简单的方式,计算查询向量与所有向量的距离,返回最近的 K 个。

  • 时间复杂度\(O(n \cdot d)\),n 为向量数,d 为维度
  • 优点:100% 精确召回
  • 缺点:规模大时不可接受
  • 适用:小数据集(< 10 万)或作为基准对比

2.2 IVF(Inverted File Index)

将向量空间用 K-Means 聚类划分为多个 Voronoi 单元,查询时只搜索最近的几个单元。

Text Only
训练阶段:
1. 对所有向量执行 K-Means 聚类 → nlist 个聚类中心
2. 每个向量分配到最近的聚类

查询阶段:
1. 找到查询向量最近的 nprobe 个聚类中心
2. 只在这 nprobe 个聚类内搜索

关键参数: - nlist:聚类数量(通常 \(\sqrt{n}\)\(4\sqrt{n}\)) - nprobe:查询时搜索的聚类数(越大越精确,越慢)

2.3 HNSW(Hierarchical Navigable Small World)

基于跳表 + 小世界图的层次化数据结构,是目前最流行的 ANN 算法之一。

Text Only
构建阶段:
Layer 3:  [A] ------------- [D]              (稀疏,长距离跳转)
Layer 2:  [A] ----- [C] --- [D]
Layer 1:  [A] - [B] - [C] - [D] - [E]
Layer 0:  [A]-[B]-[C]-[D]-[E]-[F]-[G]-[H]    (稠密,所有节点)

查询阶段:
1. 从最高层入口节点开始
2. 在当前层贪心搜索最近邻
3. 下降到下一层,重复搜索
4. 在最底层返回 Top-K 结果

关键参数: - M:每个节点的最大边数(通常 16-64) - ef_construction:构建时的候选列表大小(越大索引质量越高) - ef_search:查询时的候选列表大小(越大召回越高)

优缺点: - ✅ 查询速度极快(对数复杂度) - ✅ 召回率高(通常 > 95%) - ❌ 内存占用大(需存储图结构) - ❌ 构建速度相对较慢

2.4 PQ(Product Quantization)

通过向量压缩大幅降低内存开销,适合超大规模场景。

Text Only
原理:
1. 将 D 维向量切分为 m 个子向量
2. 对每个子空间独立执行 K-Means(通常 K=256)
3. 每个子向量用 1 字节编码(聚类 ID)
4. 原始向量 D×4 bytes → m bytes

示例(D=128,m=8):
原始:128 × 4 = 512 bytes
压缩:8 bytes(压缩比 64x)

2.5 常用索引组合

索引类型 内存占用 查询速度 召回率 适用规模
Flat 100% < 10 万
IVF_Flat 95%+ 10-1000 万
HNSW 很高 很快 98%+ 10-5000 万
IVF_PQ 90%+ 1000 万-10 亿
IVF_HNSW 很快 97%+ 100-5000 万
HNSW_PQ 很快 93%+ 5000 万-10 亿

3. Milvus 实战

3.1 安装与启动

Bash
# Docker Compose 方式(推荐)
wget https://github.com/milvus-io/milvus/releases/download/v2.6.11/milvus-standalone-docker-compose.yml -O docker-compose.yml
docker compose up -d

# 验证启动
docker compose ps

# 安装 Python SDK
pip install pymilvus==2.6.11

3.2 连接与建集合

Python
from pymilvus import (
    connections, Collection, FieldSchema,
    CollectionSchema, DataType, utility
)

# 连接 Milvus
connections.connect("default", host="localhost", port="19530")

# 定义字段
fields = [
    FieldSchema(name="id", dtype=DataType.INT64,
                is_primary=True, auto_id=True),
    FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=512),
    FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768),
]

# 创建集合
schema = CollectionSchema(fields, description="文档向量集合")
collection = Collection("documents", schema)
print(f"集合创建成功: {collection.name}")

3.3 创建索引

Python
# 创建 HNSW 索引
index_params = {
    "index_type": "HNSW",
    "metric_type": "COSINE",  # 余弦相似度
    "params": {
        "M": 32,               # 每个节点的边数
        "efConstruction": 256  # 构建质量
    }
}
collection.create_index("embedding", index_params)
print("索引创建完成")

# 加载到内存
collection.load()

3.4 插入数据

Python
import numpy as np
from sentence_transformers import SentenceTransformer

# 加载 Embedding 模型
model = SentenceTransformer("BAAI/bge-base-zh-v1.5")

# 准备数据
documents = [
    "向量数据库是 AI 时代的关键基础设施",
    "Milvus 支持十亿级向量的高效检索",
    "HNSW 算法在召回率和速度之间取得了良好平衡",
    "RAG 系统将检索与生成相结合",
    "Product Quantization 可以大幅压缩向量存储",
]

# 生成 Embedding
embeddings = model.encode(documents).tolist()

# 插入数据
data = [documents, embeddings]
result = collection.insert(data)
print(f"插入 {result.insert_count} 条记录")

# 刷新确保数据持久化
collection.flush()

3.5 向量搜索

Python
# 查询向量
query = "如何优化大规模向量检索?"
query_embedding = model.encode([query]).tolist()

# 执行搜索
search_params = {
    "metric_type": "COSINE",
    "params": {"ef": 128}  # HNSW 搜索参数
}

results = collection.search(
    data=query_embedding,
    anns_field="embedding",
    param=search_params,
    limit=3,
    output_fields=["title"]
)

# 输出结果
for hits in results:
    for hit in hits:
        print(f"ID: {hit.id}, 距离: {hit.distance:.4f}, "
              f"标题: {hit.entity.get('title')}")

3.6 混合查询(标量过滤 + 向量搜索)

Python
# 带过滤条件的搜索
results = collection.search(
    data=query_embedding,
    anns_field="embedding",
    param=search_params,
    limit=5,
    expr='category == "tech" and publish_year >= 2024',
    output_fields=["title", "category", "publish_year"]
)

3.7 删除与清理

Python
# 按条件删除
collection.delete('id in [1, 2, 3]')

# 删除集合
utility.drop_collection("documents")

# 断开连接
connections.disconnect("default")

4. RAG 系统中的向量数据库集成

4.1 RAG 架构概览

Text Only
┌─────────────────────────────────────────────┐
│                 RAG Pipeline                 │
├──────────────────┬──────────────────────────┤
│   离线索引阶段    │      在线查询阶段         │
│                  │                          │
│  文档 → 分块     │  用户问题                 │
│    ↓             │    ↓                     │
│  Embedding 编码  │  Embedding 编码           │
│    ↓             │    ↓                     │
│  存入向量数据库   │  向量数据库检索 Top-K      │
│                  │    ↓                     │
│                  │  拼接 Prompt + 检索结果    │
│                  │    ↓                     │
│                  │  LLM 生成回答             │
└──────────────────┴──────────────────────────┘

4.2 完整 RAG 示例(LangChain + Milvus)

Python
from langchain_community.vectorstores import Milvus
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
from langchain_openai import ChatOpenAI
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate

# 1. 加载文档
loader = TextLoader("knowledge_base.txt", encoding="utf-8")
documents = loader.load()

# 2. 文档分块
splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separators=["\n\n", "\n", "。", "!", "?", ".", " "]
)
chunks = splitter.split_documents(documents)
print(f"文档分为 {len(chunks)} 个块")

# 3. Embedding + 存入 Milvus
embeddings = HuggingFaceBgeEmbeddings(
    model_name="BAAI/bge-base-zh-v1.5"
)

vectorstore = Milvus.from_documents(
    documents=chunks,
    embedding=embeddings,
    connection_args={"host": "localhost", "port": "19530"},
    collection_name="rag_knowledge",
    drop_old=True
)

# 4. 构建 RAG 链(LCEL,替代已废弃的 RetrievalQA)
llm = ChatOpenAI(model="gpt-4o", temperature=0)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

system_prompt = """基于以下上下文回答问题。如果上下文中没有相关信息,请说"我不知道"。

{context}"""

prompt = ChatPromptTemplate.from_messages([
    ("system", system_prompt),
    ("human", "{input}")
])

question_answer_chain = create_stuff_documents_chain(llm, prompt)
rag_chain = create_retrieval_chain(retriever, question_answer_chain)

# 5. 查询
response = rag_chain.invoke({"input": "向量数据库的核心优势是什么?"})
print(response["answer"])
for doc in response["context"]:
    print(f"  来源: {doc.metadata.get('source', 'N/A')}")

4.3 分块策略对比

策略 块大小 适用场景 注意事项
固定大小 200-500 tokens 通用 可能切断语义
递归分割 500-1000 chars 结构化文档 优先在段落边界切分
语义分割 动态 高质量需求 需额外模型,速度慢
按句子 1-3 句 问答场景 块小,检索噪音大
父子分块 分层 长文档 检索小块,返回父块上下文

5. 性能优化

5.1 分片与部署

YAML
# Milvus 分布式部署(Kubernetes Helm)
# values.yaml 关键配置
cluster:
  enabled: true

queryNode:
  replicas: 3           # 查询节点副本数
  resources:
    limits:
      memory: "16Gi"
      cpu: "8"

dataNode:
  replicas: 2

proxy:
  replicas: 2

minio:                   # 对象存储
  mode: distributed
  replicas: 4

etcd:                    # 元数据存储
  replicaCount: 3

5.2 索引调优指南

Python
# 场景1:高召回率优先(推荐系统)
index_params_high_recall = {
    "index_type": "HNSW",
    "metric_type": "COSINE",
    "params": {"M": 64, "efConstruction": 512}
}
search_params_high_recall = {"ef": 256}

# 场景2:低延迟优先(实时搜索)
index_params_low_latency = {
    "index_type": "IVF_FLAT",
    "metric_type": "L2",
    "params": {"nlist": 2048}
}
search_params_low_latency = {"nprobe": 16}

# 场景3:大规模低成本(十亿级)
index_params_large_scale = {
    "index_type": "IVF_PQ",
    "metric_type": "L2",
    "params": {"nlist": 4096, "m": 16, "nbits": 8}
}
search_params_large_scale = {"nprobe": 64}

5.3 混合检索(向量 + 关键词)

Python
from pymilvus import AnnSearchRequest, RRFRanker

# 向量搜索请求
vector_req = AnnSearchRequest(
    data=query_embedding,
    anns_field="dense_embedding",
    param={"metric_type": "COSINE", "params": {"ef": 128}},
    limit=20
)

# 稀疏向量搜索(BM25 / SPLADE)
sparse_req = AnnSearchRequest(
    data=sparse_query_embedding,
    anns_field="sparse_embedding",
    param={"metric_type": "IP"},
    limit=20
)

# RRF 融合排序
results = collection.hybrid_search(
    reqs=[vector_req, sparse_req],
    ranker=RRFRanker(k=60),
    limit=10,
    output_fields=["title", "content"]
)

5.4 性能优化清单

优化项 措施 预期效果
Embedding 模型 选择合适维度(384/768 vs 1536) 降低存储和计算开销
批量插入 每批 1000-10000 条 提升写入吞吐 5-10x
索引预热 查询前调用 collection.load() 避免首次查询延迟
分区键 按 tenant_id 分区 缩小搜索范围
标量索引 对过滤字段创建索引 加速混合查询
连接池 复用 Milvus 连接 减少连接开销
结果缓存 Redis 缓存热门查询 减少重复计算

6. 面试高频题

Q1: 向量数据库和传统数据库的核心区别?

传统数据库基于精确匹配(B-Tree/Hash),适合结构化查询;向量数据库基于近似最近邻搜索(ANN),适合语义相似性检索。核心区别在于查询语义:精确 vs 近似、关键词 vs 语义。

Q2: HNSW 的基本原理和时间复杂度?

HNSW 构建一个层次化的小世界图。构建时间 \(O(n \log n)\),查询时间 \(O(\log n)\)。通过从高层(稀疏长距离连接)逐层下降到底层(稠密短距离连接)实现高效搜索。

Q3: 如何选择向量索引类型?

  • 小数据 (< 10万):Flat(精确搜索)
  • 高召回需求:HNSW(内存允许时首选)
  • 大规模低成本:IVF_PQ(内存受限)
  • 平衡方案:IVF_FLAT 或 IVF_HNSW

Q4: RAG 系统中如何优化检索质量?

  1. 选择合适的分块策略和大小
  2. 使用高质量 Embedding 模型(如 BGE/GTE 系列)
  3. 混合检索(稠密 + 稀疏向量)
  4. 重排序(Cross-Encoder Reranker)
  5. 多路召回 + 融合排序(RRF)

Q5: Milvus 如何实现水平扩展?

Milvus 采用存储-计算分离架构:Proxy 负责路由,QueryNode 负责查询(可水平扩展),DataNode 负责写入,底层使用 MinIO/S3 做对象存储、etcd 做元数据管理。通过增加 QueryNode 副本数线性提升查询吞吐。

Q6: PQ 量化会损失多少精度?如何弥补?

PQ 通常导致 5-10% 的召回率下降。弥补方法: - 增加 nprobe(搜索更多聚类) - 使用 OPQ(Optimized PQ)旋转优化 - 结合 Refine 步骤(用原始向量重排 Top-K) - IVF_PQ + HNSW_PQ 组合索引


✅ 检查清单

  • 理解向量数据库的核心概念和使用场景
  • 能对比 Milvus/Pinecone/Chroma/Qdrant/Weaviate 的优劣势
  • 掌握 HNSW、IVF、PQ 三种核心索引算法的原理
  • 能用 Milvus Python SDK 完成建表、插入、搜索全流程
  • 理解 RAG 系统中向量数据库的角色与集成方式
  • 能根据场景选择合适的索引类型和参数
  • 了解混合检索(稠密 + 稀疏)的实现方式
  • 掌握分布式部署和性能调优的基本策略
  • 能回答面试中关于向量数据库的常见问题