文章摘要
加载中...|
此内容根据文章生成,并经过人工审核,仅用于文章内容的解释与总结 投诉

概述

在上一篇文章中,我们学习了 RAG 技术。向量数据库是 RAG 系统的核心组件,它负责存储和检索文档的向量表示。本文将深入介绍向量数据库的原理、主流产品对比和实战部署。

什么是向量数据库

传统数据库 vs 向量数据库

text
┌─────────────────────────────────────────────────────────┐
│               传统数据库 vs 向量数据库                     │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  传统数据库 (MySQL, PostgreSQL)                          │
│  ├── 精确匹配:WHERE name = "张三"                      │
│  ├── 结构化数据:行和列                                   │
│  └── 不适合语义搜索                                       │
│                                                         │
│  向量数据库 (Pinecone, Milvus)                           │
│  ├── 近似搜索:找最相似的向量                             │
│  ├── 非结构化数据:文本、图像、音频                         │
│  └── 支持语义搜索                                         │
│                                                         │
└─────────────────────────────────────────────────────────┘

为什么需要向量数据库

问题传统数据库向量数据库
语义搜索❌ 难以实现✅ 原生支持
相似度匹配❌ 效率低✅ 高效 ANN
高维数据❌ 性能差✅ 专门优化
大规模检索❌ 全表扫描✅ 索引加速

向量数据库的核心功能

text
┌─────────────────────────────────────────────────────────┐
│                    向量数据库功能                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  1. 向量存储                                              │
│     • 存储高维向量(512/1024/1536 维)                    │
│     • 关联元数据(文档 ID、标签等)                        │
│                                                         │
│  2. 向量索引                                              │
│     • HNSW、IVF、PQ 等索引算法                           │
│     • 加速近似最近邻搜索                                  │
│                                                         │
│  3. 相似度搜索                                            │
│     • KNN 搜索(K 近邻)                                  │
│     • 范围搜索                                            │
│     • 过滤搜索                                            │
│                                                         │
│  4. 实时更新                                              │
│     • 插入、删除、更新向量                                │
│     • 增量索引更新                                        │
│                                                         │
│  5. 横向扩展                                              │
│     • 分布式架构                                         │
│     • 数据分片                                           │
│                                                         │
└─────────────────────────────────────────────────────────┘

向量嵌入(Embedding)原理

什么是 Embedding

Embedding 将文本、图像等数据转换为固定长度的数值向量。

text
┌─────────────────────────────────────────────────────────┐
│                    Embedding 示例                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  文本: "人工智能改变世界"                                  │
│        │                                                 │
│        │ Embedding 模型                                  │
│        ▼                                                 │
│  向量: [0.12, -0.34, 0.56, ..., 0.78]  (1536 维)         │
│                                                         │
│  相似概念的向量在空间中距离更近:                          │
│                                                         │
│              猫 ──────── 狗                               │
│                \       /                                 │
│                 \     /                                  │
│                  \   /                                   │
│                   动物                                    │
│                                                         │
│                  汽车                                     │
│                                                         │
└─────────────────────────────────────────────────────────┘

主流 Embedding 模型

模型维度特点适用场景
text-embedding-3-small1536OpenAI,平衡性价比通用
text-embedding-3-large3072OpenAI,最高质量高精度需求
bge-large-zh-v1.51024中文优化中文应用
e5-large-v21024多语言多语言场景
all-MiniLM-L6-v2384轻量级本地部署

生成 Embedding

python
# OpenAI Embeddings
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(
    model="text-embedding-3-small",
    dimensions=1536
)

# 单个文本
vector = embeddings.embed_query("什么是向量数据库?")
print(f"维度: {len(vector)}")

# 批量文本
texts = ["文本1", "文本2", "文本3"]
vectors = embeddings.embed_documents(texts)

# 本地模型
from langchain_community.embeddings import HuggingFaceEmbeddings

local_embeddings = HuggingFaceEmbeddings(
    model_name="BAAI/bge-large-zh-v1.5",
    model_kwargs={'device': 'cuda'},  # GPU 加速
    encode_kwargs={'normalize_embeddings': True}
)

相似度计算方法

1. 余弦相似度(推荐)

python
import numpy as np

def cosine_similarity(a, b):
    """余弦相似度:范围 [-1, 1],越接近 1 越相似"""
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

# 示例
vec1 = np.array([1, 2, 3])
vec2 = np.array([1.1, 2.1, 2.9])

similarity = cosine_similarity(vec1, vec2)
print(f"余弦相似度: {similarity}")

特点:

  • 只关心方向,不关心大小
  • 范围 [-1, 1]
  • 对向量长度不敏感

2. 欧氏距离

python
def euclidean_distance(a, b):
    """欧氏距离:越小越相似"""
    return np.linalg.norm(a - b)

# 示例
distance = euclidean_distance(vec1, vec2)
print(f"欧氏距离: {distance}")

特点:

  • 考虑向量的绝对位置
  • 范围 [0, +∞]
  • 对向量长度敏感

3. 点积

python
def dot_product(a, b):
    """点积:越大越相似"""
    return np.dot(a, b)

# 示例
dot = dot_product(vec1, vec2)
print(f"点积: {dot}")

特点:

  • 计算最快
  • 考虑方向和大小
  • 归一化后等价于余弦相似度

相似度方法对比

方法计算复杂度范围推荐场景
余弦相似度O(n)[-1, 1]通用,最常用
欧氏距离O(n)[0, ∞]需要考虑向量大小
点积O(n)(-∞, ∞)归一化向量
汉明距离O(n)[0, n]二进制向量

向量索引算法

ANN 概念

ANN(Approximate Nearest Neighbor)近似最近邻搜索,牺牲少量精度换取大幅性能提升。

text
┌─────────────────────────────────────────────────────────┐
│                  ANN vs 精确搜索                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  精确搜索 (Brute Force)                                  │
│  • 准确率: 100%                                         │
│  • 时间复杂度: O(n)                                     │
│  • 适合: 小规模数据 (<10K 向量)                          │
│                                                         │
│  ANN 搜索 (HNSW/IVF)                                     │
│  • 准确率: 95-99%                                       │
│  • 时间复杂度: O(log n)                                 │
│  • 适合: 大规模数据 (>100K 向量)                         │
│                                                         │
└─────────────────────────────────────────────────────────┘

HNSW(Hierarchical Navigable Small World)

text
┌─────────────────────────────────────────────────────────┐
│                    HNSW 结构                             │
├─────────────────────────────────────────────────────────┤
│                                                         │
│              Layer 2 (稀疏图)                            │
│              ●--------------●                           │
│             /                \                          │
│            /                  \                         │
│           ●                    ●                        │
│                                                         │
│              Layer 1 (中间层)                            │
│        ●─────────●─────●───────●                        │
│       / \        / \   / \      / \                     │
│      /   \      /   \ /   \    /   \                    │
│     ●     ●    ●     ●     ●  ●     ●                   │
│                                                         │
│              Layer 0 (底层)                              │
│     ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●                  │
│                                                         │
│  搜索过程:从高层开始,快速定位到目标区域,然后在底层精确搜索 │
│                                                         │
└─────────────────────────────────────────────────────────┘

HNSW 参数:

参数说明推荐值
ef_construction构建时搜索范围200-400
M每个节点的连接数16-32
ef_search搜索时范围50-100

IVF(Inverted File Index)

text
┌─────────────────────────────────────────────────────────┐
│                    IVF 结构                              │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  1. 将向量空间划分为多个区域 (Voronoi cells)            │
│                                                         │
│      Cluster 1  Cluster 2  Cluster 3  Cluster 4         │
│        ╱│╲          ╱│╲          ╱│╲          ╱│╲        │
│       ● ●●         ● ●●         ● ●●         ● ●●       │
│                                                         │
│  2. 搜索时只查询最近的几个 cluster                       │
│     nprobe = 2 表示查询最近的 2 个 cluster              │
│                                                         │
└─────────────────────────────────────────────────────────┘

IVF 参数:

参数说明推荐值
nlistcluster 数量√N (N 为向量总数)
nprobe搜索的 cluster 数nlist/10

PQ(Product Quantization)

text
┌─────────────────────────────────────────────────────────┐
│                    PQ 压缩                               │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  原始向量 (128 维, float32)                              │
│  = 128 × 4 bytes = 512 bytes                            │
│                                                         │
│  PQ 压缩 (8 个子空间, 256 中心点)                         │
│  = 8 × 1 byte = 8 bytes                                │
│                                                         │
│  压缩比: 64x                                             │
│  精度损失: 约 1-3%                                       │
│                                                         │
└─────────────────────────────────────────────────────────┘

索引算法对比

算法构建速度查询速度内存占用精度推荐场景
Flat100%小数据
IVF95%通用
IVF+PQ90%内存受限
HNSW很快99%高精度

主流向量数据库对比

对比总表

数据库开源语言部署特点推荐场景
PineconePython托管服务,易用快速上手
MilvusGo自建/云功能全面,高性能企业级
ChromaPython/Go本地简单易用开发测试
WeaviateGo自建/云多模态,GraphQL多模态应用
QdrantRust自建/云高性能,过滤强高并发
pgvectorCPostgreSQL关系数据库集成已有 PG

Pinecone

特点:

  • 完全托管,无需运维
  • API 简单易用
  • 自动扩展
  • 免费层有限制
python
from pinecone import Pinecone, ServerlessSpec

pc = Pinecone(api_key="your-api-key")

# 创建 Index
pc.create_index(
    name="my-index",
    dimension=1536,
    metric="cosine",
    spec=ServerlessSpec(
        cloud="aws",
        region="us-east-1"
    )
)

index = pc.Index("my-index")

# 插入向量
index.upsert([
    ("id1", [0.1, 0.2, ...], {"category": "tech"}),
    ("id2", [0.3, 0.4, ...], {"category": "news"})
])

# 搜索
results = index.query(
    vector=[0.1, 0.2, ...],
    top_k=5,
    include_metadata=True
)

Milvus

特点:

  • 开源,可自建
  • 高性能
  • 功能全面(索引、分区、复制)
  • 云原生架构
python
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType

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

# 定义 Schema
fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
    FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536),
    FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=65535)
]

schema = CollectionSchema(fields, "my_collection")

# 创建 Collection
collection = Collection("my_collection", schema)

# 插入数据
data = [
    [[0.1, 0.2, ...], [0.3, 0.4, ...]],  # embeddings
    ["文本1", "文本2"]                     # texts
]
collection.insert(data)

# 创建索引
index_params = {
    "index_type": "HNSW",
    "metric_type": "COSINE",
    "params": {"M": 16, "efConstruction": 200}
}
collection.create_index("embedding", index_params)

# 搜索
results = collection.search(
    data=[[0.1, 0.2, ...]],
    anns_field="embedding",
    param={"metric_type": "COSINE", "params": {"ef": 50}},
    limit=5
)

Chroma

特点:

  • 最简单易用
  • 本地存储
  • 适合开发和小规模部署
  • LangChain 集成良好
python
import chromadb
from chromadb.config import Settings

# 创建客户端
client = chromadb.Client(Settings(
    chroma_db_impl="duckdb+parquet",
    persist_directory="./chroma_db"
))

# 创建 Collection
collection = client.create_collection("my_collection")

# 添加文档
collection.add(
    documents=["文档1内容", "文档2内容"],
    embeddings=[[0.1, 0.2, ...], [0.3, 0.4, ...]],
    metadatas=[{"source": "doc1"}, {"source": "doc2"}],
    ids=["id1", "id2"]
)

# 查询
results = collection.query(
    query_embeddings=[[0.1, 0.2, ...]],
    n_results=5
)

Qdrant

特点:

  • Rust 实现,高性能
  • 强大的过滤功能
  • 支持混合搜索
  • 云原生
python
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct

# 连接
client = QdrantClient(url="http://localhost:6333")

# 创建 Collection
client.create_collection(
    collection_name="my_collection",
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE)
)

# 插入数据
client.upsert(
    collection_name="my_collection",
    points=[
        PointStruct(id=1, vector=[0.1, 0.2, ...], payload={"text": "文档1"}),
        PointStruct(id=2, vector=[0.3, 0.4, ...], payload={"text": "文档2"})
    ]
)

# 搜索(带过滤)
results = client.search(
    collection_name="my_collection",
    query_vector=[0.1, 0.2, ...],
    query_filter={
        "must": [
            {"key": "category", "match": {"value": "tech"}}
        ]
    },
    limit=5
)

pgvector

特点:

  • PostgreSQL 扩展
  • 关系数据库 + 向量搜索
  • 适合已有 PG 基础设施
sql
-- 安装扩展
CREATE EXTENSION vector;

-- 创建表
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    content TEXT,
    embedding vector(1536)
);

-- 创建索引
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

-- 插入数据
INSERT INTO documents (content, embedding) VALUES
    ('文档1', '[0.1,0.2,...]'::vector),
    ('文档2', '[0.3,0.4,...]'::vector);

-- 搜索
SELECT content, embedding <=> '[0.1,0.2,...]'::vector AS distance
FROM documents
ORDER BY distance
LIMIT 5;

如何选择向量数据库

决策流程

text
┌─────────────────────────────────────────────────────────┐
│                    选择决策树                             │
└─────────────────────────────────────────────────────────┘

是否已有 PostgreSQL?
├─ 是 → pgvector
└─ 否 → 是否需要托管服务?
    ├─ 是 → Pinecone
    └─ 否 → 数据规模?
        ├─ <100K 向量 → Chroma
        ├─ 100K-1M → Qdrant / Weaviate
        └─ >1M → Milvus

是否需要多模态?
├─ 是 → Weaviate
└─ 否 → 是否需要复杂过滤?
    ├─ 是 → Qdrant
    └─ 否 → Milvus / Pinecone

场景推荐

场景推荐理由
快速原型Chroma最简单,本地运行
企业生产Milvus功能全面,高性能
云原生Pinecone无需运维
多模态Weaviate原生支持
已有 PGpgvector集成简单
高并发QdrantRust 实现,性能高

实战:搭建向量数据库服务

场景 1:本地开发(Chroma)

python
import chromadb
from chromadb.config import Settings
from langchain_openai import OpenAIEmbeddings

# 1. 创建客户端
client = chromadb.PersistentClient(path="./chroma_db")

# 2. 创建 Collection
collection = client.get_or_create_collection("documents")

# 3. 初始化 Embeddings
embeddings = OpenAIEmbeddings()

# 4. 添加文档
def add_documents(texts, metadatas=None):
    vectors = embeddings.embed_documents(texts)
    collection.add(
        documents=texts,
        embeddings=vectors,
        metadatas=metadatas or [{}] * len(texts),
        ids=[f"doc_{i}" for i in range(len(texts))]
    )

# 5. 搜索
def search(query, n_results=5):
    query_vector = embeddings.embed_query(query)
    return collection.query(
        query_embeddings=[query_vector],
        n_results=n_results
    )

# 使用
add_documents(
    texts=["RAG 是检索增强生成", "向量数据库存储向量"],
    metadatas=[{"topic": "AI"}, {"topic": "DB"}]
)

results = search("什么是 RAG?")
print(results)

场景 2:生产环境(Milvus + Docker)

yaml
# docker-compose.yml
version: '3.5'

services:
  etcd:
    image: quay.io/coreos/etcd:v3.5.5
    environment:
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1000
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
    volumes:
      - ./etcd:/etcd

  minio:
    image: minio/minio:RELEASE.2023-03-20T20-16-18Z
    environment:
      MINIO_ACCESS_KEY: minioadmin
      MINIO_SECRET_KEY: minioadmin
    volumes:
      - ./minio:/minio_data
    command: minio server /minio_data

  standalone:
    image: milvusdb/milvus:v2.3.0
    command: ["milvus", "run", "standalone"]
    environment:
      ETCD_ENDPOINTS: etcd:2379
      MINIO_ADDRESS: minio:9000
    ports:
      - "19530:19530"
      - "9091:9091"
    depends_on:
      - etcd
      - minio
bash
# 启动
docker-compose up -d

# Python 客户端
pip install pymilvus
python
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
import numpy as np

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

# 定义 Schema
fields = [
    FieldSchema("id", DataType.INT64, is_primary=True),
    FieldSchema("content", DataType.VARCHAR, max_length=65535),
    FieldSchema("embedding", DataType.FLOAT_VECTOR, dim=1536)
]

schema = CollectionSchema(fields, "documents")
collection = Collection("documents", schema)

# 插入数据
def insert_documents(contents):
    from langchain_openai import OpenAIEmbeddings
    emb = OpenAIEmbeddings()

    vectors = emb.embed_documents(contents)

    data = [
        [i for i in range(len(contents))],  # ids
        contents,                            # contents
        vectors                              # embeddings
    ]

    collection.insert(data)
    collection.flush()

# 创建索引
index_params = {
    "index_type": "HNSW",
    "metric_type": "COSINE",
    "params": {"M": 16, "efConstruction": 200}
}
collection.create_index("embedding", index_params)
collection.load()

# 搜索
def search(query, top_k=5):
    from langchain_openai import OpenAIEmbeddings
    emb = OpenAIEmbeddings()

    query_vector = emb.embed_query(query)

    results = collection.search(
        data=[query_vector],
        anns_field="embedding",
        param={"metric_type": "COSINE", "params": {"ef": 50}},
        limit=top_k,
        output_fields=["content"]
    )

    return results

场景 3:云服务(Pinecone)

python
import os
from pinecone import Pinecone, ServerlessSpec
from langchain_openai import OpenAIEmbeddings

# 初始化
pc = Pinecone(api_key=os.getenv("PINECONE_API_KEY"))

# 创建 Index
pc.create_index(
    name="rag-index",
    dimension=1536,
    metric="cosine",
    spec=ServerlessSpec(
        cloud="aws",
        region="us-east-1"
    )
)

index = pc.Index("rag-index")

# 批量插入
def upsert_documents(documents):
    emb = OpenAIEmbeddings()

    vectors = []
    for i, doc in enumerate(documents):
        vector = emb.embed_query(doc["content"])
        vectors.append({
            "id": f"doc_{i}",
            "values": vector,
            "metadata": {"content": doc["content"], **doc.get("metadata", {})}
        })

    index.upsert(vectors)

# 搜索
def search(query, top_k=5, filter=None):
    emb = OpenAIEmbeddings()
    query_vector = emb.embed_query(query)

    results = index.query(
        vector=query_vector,
        top_k=top_k,
        include_metadata=True,
        filter=filter
    )

    return results

向量数据库性能优化

索引优化

python
# 1. 选择合适的索引类型

# 小规模(<10K):Flat
index_params = {"index_type": "FLAT"}

# 中等规模(10K-1M):IVF
index_params = {
    "index_type": "IVF_FLAT",
    "metric_type": "COSINE",
    "params": {"nlist": 100}
}

# 大规模(>1M):HNSW
index_params = {
    "index_type": "HNSW",
    "metric_type": "COSINE",
    "params": {"M": 16, "efConstruction": 200}
}

# 内存受限:IVF + PQ
index_params = {
    "index_type": "IVF_PQ",
    "params": {
        "nlist": 100,
        "m": 8  # PQ 子空间数
    }
}

查询优化

python
# 1. 调整 ef 参数
results = collection.search(
    data=[query_vector],
    anns_field="embedding",
    param={"metric_type": "COSINE", "params": {"ef": 100}},  # 增加 ef 提高精度
    limit=10
)

# 2. 使用过滤减少搜索空间
results = collection.search(
    data=[query_vector],
    anns_field="embedding",
    param={"metric_type": "COSINE"},
    limit=10,
    expr="category == 'tech'"  # 只搜索 tech 类别
)

# 3. 批量查询
results = collection.search(
    data=[query_vector1, query_vector2, query_vector3],  # 批量
    anns_field="embedding",
    param={"metric_type": "COSINE"},
    limit=10
)

存储优化

python
# 1. 使用量化减少内存
index_params = {
    "index_type": "IVF_PQ",
    "params": {
        "nlist": 100,
        "m": 16,           # PQ 参数
        "nbits": 8         # 每子空间位数
    }
}

# 2. 定期压缩
collection.compact()
collection.flush()

# 3. 分区存储
# 按类别分区
tech_collection = Collection("tech_docs", schema)
news_collection = Collection("news_docs", schema)

小结

向量数据库是 AI 应用的重要基础设施:

核心要点

  1. 向量数据库作用

    • 存储和检索高维向量
    • 支持语义搜索
    • 为 RAG 提供检索能力
  2. Embedding

    • 将文本转换为数值向量
    • 相似概念在空间中距离近
    • 选择合适的模型很重要
  3. 索引算法

    • HNSW:高精度,高并发
    • IVF:平衡性能和精度
    • PQ:内存优化
  4. 选型建议

    • 快速开发:Chroma
    • 企业生产:Milvus
    • 云托管:Pinecone
    • 已有 PG:pgvector
  5. 优化方向

    • 选择合适索引
    • 调整查询参数
    • 使用过滤和分区
    • 考虑量化压缩

下一篇文章将介绍 Function Calling 与工具使用,让 LLM 能够调用外部服务。

赞赏博主
评论 隐私政策