目录
[1. 什么是向量数据库?](#1. 什么是向量数据库?)
[2. 为什么需要专门的向量数据库?](#2. 为什么需要专门的向量数据库?)
[1. Faiss (Facebook AI Similarity Search)](#1. Faiss (Facebook AI Similarity Search))
[2. Milvus](#2. Milvus)
[3. Elasticsearch (v7.x+)](#3. Elasticsearch (v7.x+))
[第三部分:理解索引结构 - HNSW](#第三部分:理解索引结构 - HNSW)
[1. 核心思想:分层可导航小世界](#1. 核心思想:分层可导航小世界)
[2. 构建过程(插入)](#2. 构建过程(插入))
[3. 搜索过程(查询)](#3. 搜索过程(查询))
[4. 关键参数](#4. 关键参数)
第一部分:向量数据库核心概念
1. 什么是向量数据库?
向量数据库是专门用于存储、索引和检索高维向量 的数据库。与传统数据库基于精确匹配(如=)或文本搜索(如LIKE)不同,向量数据库的核心是基于相似性搜索。
- 向量: 将非结构化数据(如图片、文本、音频、视频)通过深度学习模型(如BERT, ResNet, CLIP)转化为的数值数组。例如,一个句子可能被表示为768维的向量。
- 相似性: 通过计算向量之间的距离(如欧氏距离、余弦相似度)来衡量其相关性。距离越小,相似度越高。
- 核心能力 :从海量高维向量中,快速找到与目标向量最相似的K个向量。
2. 为什么需要专门的向量数据库?
随着大模型和AI应用(如RAG、推荐系统、图像搜索)的爆发,传统数据库在处理向量相似性搜索时面临挑战:
- 性能瓶颈: 在海量数据(百万、亿级)中进行"最近邻"搜索是计算密集型操作,传统索引(如B树)效率极低。
- 精度与速度的权衡 : 精确的K近邻(K-NN)搜索复杂度极高,需要近似最近邻(ANN)搜索在可接受的精度损失下,将搜索速度提升数个数量级。
- 数据管理: 需要管理向量及其关联的原始数据(元数据),并提供增删改查、持久化、高可用等数据库特性。
第二部分:核心工具详解
这三个工具代表了向量处理领域的三种不同范式。
1. Faiss (Facebook AI Similarity Search)
定位 :一个专注于向量相似性搜索的库,而非完整数据库。
- 核心特性 :
- 纯C++/Python库: 直接集成到应用中,无服务器进程。
- 算法丰富: 提供多种索引类型(Flat, IVF, HNSW, PQ等)和精确/近似搜索算法。
- 极致性能: 高度优化,支持GPU加速,是学术研究和工业界的性能基准。
- 无数据管理: 不提供持久化(需自己保存索引文件)、无元数据管理、无分布式支持(有基础集群方案)。
- 部署与使用 :
- 部署 :
pip install faiss-cpu或faiss-gpu。本质上是导入一个Python库。 - 典型使用流程:
- 部署 :
python
import faiss
import numpy as np
# 1. 生成数据
d = 128 # 向量维度
nb = 100000 # 数据库大小
x = np.random.random((nb, d)).astype('float32')
# 2. 构建索引(以HNSW为例)
index = faiss.IndexHNSWFlat(d, 32) # 32为构建参数
index.add(x)
# 3. 搜索
k = 10 # 返回最近邻数量
query = np.random.random((1, d)).astype('float32')
distances, indices = index.search(query, k)
print(indices, distances)
# [[31680 40123 93393 67177 59492 78627 3882 43555 93068 19121]] [[13.229351 14.017465 14.350857 14.728469 14.749828 14.847415 14.975938 14.987753 15.016882 15.203573]]
- 适用场景: 研究原型、需要极致性能的嵌入式系统、作为其他系统(如Milvus)的底层引擎。
2. Milvus
定位 :云原生、功能完备的开源向量数据库。
- 核心特性 :
- 数据管理: 支持完整的CRUD操作、数据持久化、快照、数据压缩。
- 元数据管理 : 通过集合(Collection)和分区(Partition)组织数据,支持标量字段过滤(如
where price < 100)。 - 高性能ANN: 集成多种索引(Faiss, HNSW, ANNOY等),自动执行查询优化。
- 分布式与高可用: 采用存算分离架构(对象存储 + 消息队列 + 协调服务),易于水平扩展。
- 丰富的SDK和工具: 支持多种语言,提供图形化管理工具(Attu)。
- 部署与使用 :
- 部署 :
- 单机版(开发): 使用Docker Compose快速启动所有组件。
- 集群版(生产): 使用Kubernetes Helm Chart或云服务(Zilliz Cloud)。
- 典型使用流程(PyMilvus SDK):
- 部署 :
python
# 安装 Milvus Lite
# pip install milvus pymilvus
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType, utility
from milvus import default_server
import numpy as np
# 启动内置服务器
default_server.start()
# 连接
connections.connect(host='127.0.0.1', port=default_server.listen_port)
# 检查集合是否存在,如果存在则删除
collection_name = "my_news"
if utility.has_collection(collection_name):
print(f"集合 '{collection_name}' 已存在,正在删除...")
utility.drop_collection(collection_name)
# 2. 定义集合模式(向量+元数据)
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=128),
FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=200)
]
schema = CollectionSchema(fields, description="news collection")
collection = Collection("my_news", schema)
# 3. 创建索引
index_params = {
"index_type": "HNSW",
"metric_type": "L2",
"params": {"M": 16, "efConstruction": 200}
}
collection.create_index("embedding", index_params)
# 4. 插入数据
# 生成10000条模拟数据
num_records = 10000
ids = list(range(num_records))
embeddings = np.random.random((num_records, 128)).tolist()
titles = [f"title_{i}" for i in range(num_records)]
# 准备插入数据
data = [embeddings, titles]
collection.insert(data)
# 将集合加载到内存
collection.load()
# 5. 混合搜索(向量相似度 + 标量过滤)
# 首先创建查询向量(这里随机生成一个查询向量)
query_vector = np.random.random((1, 128)).tolist() # 单个查询向量
# 设置搜索参数
search_params = {"metric_type": "L2", "params": {"ef": 100}}
# 执行搜索
results = collection.search(
data=query_vector, # 查询向量
anns_field="embedding", # 向量字段名
param=search_params, # 搜索参数
limit=10, # 返回结果数量
expr="title like 'title_5%'" # 标量过滤条件
)
# 输出搜索结果
print("搜索结果:")
for hits in results:
for hit in hits:
print(f"id: {hit.id}, 距离: {hit.distance}, 标题: {hit.entity.get('title')}")
# 6. 清理资源(可选)
# collection.drop()
# 停止服务器(可选)
# default_server.stop()
结果:
bash
集合 'my_news' 已存在,正在删除...
搜索结果:
id: 463685161034454964, 距离: 15.321002006530762, 标题: None
id: 463685161034454646, 距离: 15.373628616333008, 标题: None
id: 463685161034454432, 距离: 15.433990478515625, 标题: None
id: 463685161034454209, 距离: 15.609135627746582, 标题: None
id: 463685161034454146, 距离: 15.669434547424316, 标题: None
id: 463685161034454907, 距离: 15.885712623596191, 标题: None
id: 463685161034454823, 距离: 15.902074813842773, 标题: None
id: 463685161034454500, 距离: 16.118179321289062, 标题: None
id: 463685161034454563, 距离: 16.18042755126953, 标题: None
id: 463685161034454598, 距离: 16.20553207397461, 标题: None
- 适用场景: 生产级AI应用(如RAG、内容推荐、欺诈检测),需要管理海量向量和元数据,并追求高可用和可扩展性。
3. Elasticsearch (v7.x+)
定位 :在传统全文搜索引擎基础上扩展了向量搜索能力的通用搜索平台。
- 核心特性 :
- 混合搜索 : 将向量搜索 与关键词搜索 、过滤器 、聚合分析无缝结合能力是其最大优势。
- 强大的生态: 成熟的ELK栈,提供日志、监控、安全等全方位企业级功能。
- 数据管理: 本身就是成熟的文档数据库,具备完整的数据管理、分布式和高可用能力。
- 向量能力 : 从7.x版本开始支持
dense_vector类型,并内置HNSW索引支持。
- 部署与使用 :
- 部署: 通过Docker、tar包或云服务部署Elasticsearch集群。
- 典型使用流程:
(1)创建带向量字段的映射: 在Elasticsearch中创建一个名为"my_index"的索引,并定义其映射(mapping)。映射中指定了索引中文档的字段及其类型和属性。
bash
PUT my_index
{
"mappings": {
"properties": {
"content": { "type": "text" },
"embedding": {
"type": "dense_vector",
"dims": 128,
"index": true,
"similarity": "cosine",
"index_options": {
"type": "hnsw",
"m": 16,
"ef_construction": 200
}
}
}
}
}
具体来说,这个映射定义了两个字段:
- "content"字段:类型为"text",这意味着它是一个文本字段,会进行分词处理,用于全文搜索。
- "embedding"字段:类型为"dense_vector",这是一个密集向量类型,用于存储向量数据。这里指定了向量的维度(dims)为128,并且设置了该字段将被索引(index: true)以便进行相似度搜索。相似度度量方式为余弦相似度(cosine)。此外,还指定了索引选项(index_options)使用HNSW(Hierarchical Navigable Small World)算法进行近似最近邻搜索,其中参数m(构建图时每个节点连接的边数)设置为16,ef_construction(构建图时考虑的候选节点数量)设置为200。
总结:这段代码创建了一个用于向量相似度搜索的索引,可以存储文本内容和对应的128维向量,并支持使用余弦相似度进行近似最近邻搜索。
(2)插入文档 : 像插入普通JSON文档一样,包含embedding字段。
(3)执行混合搜索:
bash
GET my_index/_search
{
"query": {
"script_score": {
"query": { "match": { "content": "机器学习" } }, // 文本查询
"script": {
"source": "cosineSimilarity(params.query_vector, 'embedding') + 1.0",
"params": { "query_vector": [0.12, 0.34, ...] }
}
}
}
}
检索主要步骤:
- 首先,使用一个基础的文本查询(match query)在"content"字段中查找包含"机器学习"的文档。这一步会得到一个基础查询的分数。
- 然后,使用脚本(script)计算每个文档的向量字段(embedding)与给定查询向量(query_vector)的余弦相似度,并加1.0(为了避免负分)。
- 最后,将基础查询的分数与脚本计算出的余弦相似度分数相乘(script_score的默认操作是相乘)得到每个文档的最终分数。
- 适用场景 : 已在使用ES生态,或对文本+向量的混合搜索需求极强的应用(如电商搜索、企业知识库、日志分析)。
第三部分:理解索引结构 - HNSW
HNSW是当前最流行、综合性能最佳的ANN索引之一,被Faiss、Milvus、Elasticsearch等广泛采用。
1. 核心思想:分层可导航小世界
- 小世界网络: 灵感来自社交网络(六度分隔理论)。在这种网络中,任意两个节点都可以通过少量几步连接。通过构建这种网络,可以在搜索时快速"跳跃"到目标区域。
- 分层结构: 构建一个多层的图结构(类似跳表)。底层(第0层)包含所有数据点,越往上,节点越稀疏,连接是长距离的"高速公路"。
2. 构建过程(插入)
- 随机选择层数 : 为新插入的节点
q分配一个最高层l(指数衰减概率分布,确保高层节点极少)。 - 从顶层开始查找 :
- 从顶层的一个入口点开始。
- 在当前层,贪婪地移动到最接近
q的邻居节点,直到无法更接近。
- 逐层向下搜索并连接 :
- 重复步骤2,直到达到第
l层。 - 在第
l层找到q的efConstruction个最近邻,然后将q与这些邻居中的一部分(由M参数控制最大连接数)建立双向连接。
- 重复步骤2,直到达到第
3. 搜索过程(查询)
- 从顶层入口点开始。
- 贪婪遍历: 在当前层,从当前已知的最近邻集合中,不断移动到更接近查询目标的邻居,直到局部最优。
- 进入下一层: 将当前层的局部最优结果作为下一层的入口点,重复步骤2。
- 在底层进行精细搜索 : 到达第0层后,使用一个动态候选列表(大小为
efSearch)进行更精细的搜索,最终返回列表中的最近邻。
4. 关键参数
- M: 每个节点在第0层以上的最大连接数。值越大,图越稠密,精度越高,但内存占用和构建时间也增加。
- efConstruction: 构建时动态候选列表的大小。值越大,构建质量越高,构建越慢。
- efSearch: 搜索时动态候选列表的大小。值越大,搜索精度越高,搜索越慢。
直观比喻: 查找一本图书馆的书。
- Flat(暴力搜索): 从第一排书架开始,逐本比对。
- HNSW : 先看楼层索引(顶层),找到"科技类"区域(快速跳跃),再看"计算机"分区(中层),最后在"AI书籍"书架(底层)仔细寻找。
efSearch决定了你在最终书架上要仔细翻看多少本书后才做决定。
第四部分:对比与总结
|-------------|--------------|-----------------|---------------------|
| 特性 | Faiss | Milvus | Elasticsearch |
| 定位 | 搜索引擎库 | 向量数据库 | 通用搜索平台 |
| 核心优势 | 极致性能,算法灵活 | 功能完备,云原生,可扩展 | 文本+向量混合搜索,生态强大 |
| 数据管理 | 无 | 强(CRUD,元数据) | 强(文档数据库) |
| 持久化/高可用 | 需自行处理 | 内置 | 内置 |
| 部署复杂度 | 极低(库) | 中等(Docker/K8s) | 中等(集群) |
| 学习成本 | 低(API简单) | 中(概念较多) | 高(DSL复杂,生态庞大) |
| 典型场景 | 算法研究,嵌入到其他系统 | 生产AI应用,RAG,AIGC | 企业搜索,混合检索,已有ES生态的应用 |
如何选择?
- 追求简单、高性能的离线或嵌入式场景 : 选择Faiss。
- 构建全新的、以向量为中心的AI应用,需要生产级特性 : 选择Milvus。
- 已有Elasticsearch,或对关键词与向量的混合搜索需求是核心 : 选择Elasticsearch。
- 学习入门 : 可以从Faiss 开始理解向量搜索的基本原理,然后转向Milvus以了解完整的向量数据库概念。