文章摘要 FakeGPT
加载中...|
概述
在上一篇文章中,我们学习了 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-small | 1536 | OpenAI,平衡性价比 | 通用 |
| text-embedding-3-large | 3072 | OpenAI,最高质量 | 高精度需求 |
| bge-large-zh-v1.5 | 1024 | 中文优化 | 中文应用 |
| e5-large-v2 | 1024 | 多语言 | 多语言场景 |
| all-MiniLM-L6-v2 | 384 | 轻量级 | 本地部署 |
生成 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 参数:
| 参数 | 说明 | 推荐值 |
|---|---|---|
nlist | cluster 数量 | √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% │
│ │
└─────────────────────────────────────────────────────────┘索引算法对比
| 算法 | 构建速度 | 查询速度 | 内存占用 | 精度 | 推荐场景 |
|---|---|---|---|---|---|
| Flat | 快 | 慢 | 高 | 100% | 小数据 |
| IVF | 中 | 中 | 中 | 95% | 通用 |
| IVF+PQ | 中 | 快 | 低 | 90% | 内存受限 |
| HNSW | 慢 | 很快 | 高 | 99% | 高精度 |
主流向量数据库对比
对比总表
| 数据库 | 开源 | 语言 | 部署 | 特点 | 推荐场景 |
|---|---|---|---|---|---|
| Pinecone | ❌ | Python | 云 | 托管服务,易用 | 快速上手 |
| Milvus | ✅ | Go | 自建/云 | 功能全面,高性能 | 企业级 |
| Chroma | ✅ | Python/Go | 本地 | 简单易用 | 开发测试 |
| Weaviate | ✅ | Go | 自建/云 | 多模态,GraphQL | 多模态应用 |
| Qdrant | ✅ | Rust | 自建/云 | 高性能,过滤强 | 高并发 |
| pgvector | ✅ | C | PostgreSQL | 关系数据库集成 | 已有 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 | 原生支持 |
| 已有 PG | pgvector | 集成简单 |
| 高并发 | Qdrant | Rust 实现,性能高 |
实战:搭建向量数据库服务
场景 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
- miniobash
# 启动
docker-compose up -d
# Python 客户端
pip install pymilvuspython
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 应用的重要基础设施:
核心要点
向量数据库作用
- 存储和检索高维向量
- 支持语义搜索
- 为 RAG 提供检索能力
Embedding
- 将文本转换为数值向量
- 相似概念在空间中距离近
- 选择合适的模型很重要
索引算法
- HNSW:高精度,高并发
- IVF:平衡性能和精度
- PQ:内存优化
选型建议
- 快速开发:Chroma
- 企业生产:Milvus
- 云托管:Pinecone
- 已有 PG:pgvector
优化方向
- 选择合适索引
- 调整查询参数
- 使用过滤和分区
- 考虑量化压缩
下一篇文章将介绍 Function Calling 与工具使用,让 LLM 能够调用外部服务。
赞赏博主
评论 隐私政策