随着大语言模型、语义搜索和智能推荐的发展,传统关键词检索已经难以满足"理解语义"的需求。向量数据库通过保存和检索 Embedding,让计算机可以根据内容的含义查找相似数据。
文章目录
-
- 一、什么是向量数据库
- 二、为什么普通数据库不够用
- [三、什么是 Embedding](#三、什么是 Embedding)
- 四、向量相似度如何计算
-
- [1. 余弦相似度](#1. 余弦相似度)
- [2. 欧氏距离](#2. 欧氏距离)
- [3. 内积](#3. 内积)
- [4. 如何选择](#4. 如何选择)
- 五、精确搜索与近似搜索
-
- [1. 精确最近邻搜索](#1. 精确最近邻搜索)
- [2. 近似最近邻搜索](#2. 近似最近邻搜索)
- 六、常见向量索引
-
- [1. HNSW](#1. HNSW)
- [2. IVFFlat](#2. IVFFlat)
- 七、向量数据库保存什么数据
- 八、向量检索的完整流程
- [九、使用 PostgreSQL 和 pgvector](#九、使用 PostgreSQL 和 pgvector)
-
- [1. 启用扩展](#1. 启用扩展)
- [2. 创建文档表](#2. 创建文档表)
- [3. 写入向量](#3. 写入向量)
- [4. 余弦距离查询](#4. 余弦距离查询)
- [5. 创建 HNSW 索引](#5. 创建 HNSW 索引)
- [6. 创建 IVFFlat 索引](#6. 创建 IVFFlat 索引)
- [十、Java 查询示例](#十、Java 查询示例)
- [十一、向量数据库在 RAG 中的作用](#十一、向量数据库在 RAG 中的作用)
- 十二、文档应该如何分段
-
- [1. 固定长度分段](#1. 固定长度分段)
- [2. 按标题和段落分段](#2. 按标题和段落分段)
- [3. 递归分段](#3. 递归分段)
- [4. 重叠窗口](#4. 重叠窗口)
- 十三、为什么需要混合检索
- [十四、Metadata 过滤的重要性](#十四、Metadata 过滤的重要性)
- 十五、常见向量数据库选型
-
- [1. pgvector](#1. pgvector)
- [2. Milvus](#2. Milvus)
- [3. Qdrant](#3. Qdrant)
- [4. Elasticsearch](#4. Elasticsearch)
- [5. 托管向量数据库](#5. 托管向量数据库)
- 十六、向量数据库选型指标
- 十七、项目实践建议
-
- [1. 保存原始文本](#1. 保存原始文本)
- [2. 记录模型版本](#2. 记录模型版本)
- [3. 先做小规模精确搜索基线](#3. 先做小规模精确搜索基线)
- [4. 不要只关注 Top-1](#4. 不要只关注 Top-1)
- [5. 建立离线评估集](#5. 建立离线评估集)
- [6. 对检索结果进行重排序](#6. 对检索结果进行重排序)
- [7. 做好数据更新与删除](#7. 做好数据更新与删除)
- [8. 监控索引参数](#8. 监控索引参数)
- 十八、常见误区
-
- 误区一:向量数据库可以代替关系型数据库
- [误区二:换一个向量数据库就能提高 RAG 准确率](#误区二:换一个向量数据库就能提高 RAG 准确率)
- 误区三:相似度最高的结果一定正确
- [误区四:Top-K 越大越好](#误区四:Top-K 越大越好)
- 误区五:建立索引后结果必须完全一致
- 十九、总结
一、什么是向量数据库
向量数据库是一类专门用于存储、索引和检索高维向量的数据库。
文本、图片、音频等非结构化数据经过 Embedding 模型处理后,可以转换成固定维度的数字数组:
text
"Java 是一种面向对象编程语言"
↓ Embedding 模型
[0.021, -0.135, 0.087, ..., 0.246]
这些数字本身通常没有直观含义,但在同一个 Embedding 模型生成的向量空间中,语义相近的数据通常距离也比较近。
例如:
text
"如何学习 Java?"
"Java 入门教程有哪些?"
即使两句话没有完全相同的关键词,其向量仍可能非常接近。
向量数据库负责高效完成以下工作:
- 存储向量及其原始数据;
- 建立向量索引;
- 执行相似度搜索;
- 返回距离最近的 Top-K 条数据;
- 根据分类、时间、权限等元数据过滤结果;
- 支持向量新增、更新和删除。
Qdrant 官方文档将向量描述为对象的数学表示,相似对象在向量空间中也会相互接近。Qdrant 官方文档
二、为什么普通数据库不够用
关系型数据库擅长精确匹配和范围查询:
sql
SELECT * FROM article WHERE category = 'Java';
全文检索擅长关键词搜索:
text
Java AND Spring Boot
但如果用户搜索:
text
如何提高后端接口的并发处理能力?
实际文档标题可能是:
text
CompletableFuture 异步编程实战
两者没有明显的关键词重合,但语义上存在较强关联。
传统关键词检索主要解决"是否出现这个词",向量检索则尝试解决"表达的含义是否相近"。
这也是向量数据库常用于以下场景的原因:
- 语义搜索;
- RAG 知识库问答;
- 图片相似搜索;
- 商品推荐;
- 音频检索;
- 重复内容检测;
- 异常检测;
- 多模态检索。
三、什么是 Embedding
Embedding 是把对象转换成向量的过程。
可以被向量化的对象包括:
- 单词;
- 句子;
- 文档;
- 图片;
- 音频;
- 视频;
- 用户行为;
- 商品特征。
例如:
text
苹果手机 → [0.12, 0.53, -0.27, ...]
iPhone → [0.11, 0.55, -0.25, ...]
香蕉 → [-0.61, 0.08, 0.43, ...]
"苹果手机"和"iPhone"的向量距离可能更近,而"香蕉"的向量与它们距离较远。
向量维度必须保持一致
如果集合定义的向量维度是 768,那么写入的所有向量和查询向量通常也必须是 768 维。
text
数据库中的向量:768 维
查询使用的向量:768 维
如果维度不同,就无法进行正常的距离计算。
写入与查询必须使用兼容模型
如果数据使用模型 A 生成向量,查询却使用模型 B 生成向量,即使维度相同,向量空间也可能不兼容。
因此,在实际项目中应记录:
- 模型名称;
- 模型版本;
- 向量维度;
- 归一化方式;
- 距离度量方式;
- 数据生成时间。
更换 Embedding 模型时,通常需要重新生成已有数据的向量。
四、向量相似度如何计算
向量数据库需要使用距离或相似度函数判断两个向量是否接近。
1. 余弦相似度
余弦相似度关注两个向量的方向是否一致:
text
cosine(A, B) = A · B / (|A| × |B|)
它经常用于文本语义搜索。
值越大,通常表示两个向量越相似。具体返回的是"相似度"还是"距离",需要根据数据库接口判断。
2. 欧氏距离
欧氏距离也称 L2 距离,表示两个点在空间中的直线距离:
text
L2(A, B) = √Σ(Ai - Bi)²
距离越小,表示两个向量越相近。
3. 内积
内积计算方式为:
text
A · B = Σ(Ai × Bi)
对于已经归一化的向量,内积与余弦相似度的排序结果可能一致。
4. 如何选择
一般来说:
- 文本语义检索常用余弦相似度;
- 图像或特征空间检索可能使用 L2;
- 推荐模型可能使用内积;
- 最终应以 Embedding 模型的说明为准。
不要只凭经验随意选择距离函数。模型训练使用的相似度目标,通常决定了检索时更合适的度量方式。
五、精确搜索与近似搜索
1. 精确最近邻搜索
精确搜索会将查询向量与数据库中的全部向量进行比较。
假设有 100 万条向量,每次查询都需要计算 100 万次距离,其成本会随着数据规模增大而快速上升。
精确搜索的优点是结果准确,缺点是大规模数据下速度较慢。
2. 近似最近邻搜索
ANN,即 Approximate Nearest Neighbor,会牺牲少量召回率,换取明显更高的查询速度。
它不会逐条比较所有向量,而是通过索引快速找到一批最可能相似的候选数据。
向量数据库的关键能力之一,就是在以下指标之间取得平衡:
- 查询延迟;
- 吞吐量;
- 召回率;
- 内存占用;
- 索引构建时间;
- 更新成本。
六、常见向量索引
1. HNSW
HNSW 全称 Hierarchical Navigable Small World,是一种基于多层图结构的近似最近邻索引。
可以把它理解为一张分层地图:
- 上层节点较少,用于快速进行远距离跳转;
- 下层节点更加密集,用于寻找精确邻居;
- 查询从高层开始,逐步进入更细粒度的底层。
HNSW 通常拥有较好的延迟和召回率,但索引会占用较多内存,构建过程也可能比较慢。Milvus 官方文档介绍了 HNSW 的多层图结构,以及 M、efConstruction 和查询参数 ef 对性能的影响。Milvus HNSW 文档
常见参数包括:
M:每个节点允许连接的邻居数量;efConstruction:构建索引时考虑的候选节点数量;efSearch或ef:查询时参与计算的候选数量。
参数越大,召回率通常越高,但构建时间、查询成本和内存消耗也会增加。
2. IVFFlat
IVFFlat 会先对向量进行聚类,将向量划分到不同的列表中。
查询时只搜索距离查询向量最近的若干列表,从而减少参与计算的向量数量。
与 HNSW 相比,IVFFlat 通常:
- 构建速度更快;
- 占用内存更少;
- 查询性能与召回率需要通过列表数和探测数调节;
- 建立索引前通常需要一定量的训练数据。
pgvector 官方文档指出,IVFFlat 的构建速度更快且使用更少内存,但在速度与召回率的综合表现上通常低于 HNSW。pgvector 官方说明
七、向量数据库保存什么数据
一条典型的向量记录通常包括三部分:
json
{
"id": "doc_1001",
"vector": [0.12, -0.31, 0.27],
"payload": {
"title": "Spring Boot 缓存实战",
"category": "Java",
"author": "张三",
"permission": "public"
}
}
ID
ID 用于唯一标识一条数据。
Vector
Vector 是 Embedding 模型生成的向量,用于相似度计算。
Metadata 或 Payload
元数据用于保存原始文本、分类、时间、权限等信息。
Qdrant 将基本记录称为 Point,每个 Point 包含向量以及可选的 Payload;Collection 则是一组具有相同向量配置的 Point。Qdrant 数据模型
八、向量检索的完整流程
一次典型的语义检索流程如下:
text
用户输入问题
↓
Embedding 模型生成查询向量
↓
向量数据库执行 Top-K 检索
↓
使用元数据条件过滤
↓
返回最相似的数据
例如用户输入:
text
Spring Boot 中如何减少数据库查询?
系统首先生成查询向量,然后在数据库中搜索最接近的文档,可能返回:
text
1. Spring Cache 使用教程
2. Redis 缓存设计
3. MyBatis 二级缓存原理
这些结果不一定包含与问题完全一致的关键词,但语义上与问题相关。
九、使用 PostgreSQL 和 pgvector
如果项目已经使用 PostgreSQL,并且向量规模和吞吐量要求适中,可以考虑使用 pgvector。
pgvector 支持精确搜索,也支持 HNSW 和 IVFFlat 近似索引。pgvector GitHub
1. 启用扩展
sql
CREATE EXTENSION IF NOT EXISTS vector;
2. 创建文档表
sql
CREATE TABLE document_chunk (
id BIGSERIAL PRIMARY KEY,
document_id BIGINT NOT NULL,
content TEXT NOT NULL,
category VARCHAR(50),
embedding VECTOR(768)
);
这里假设 Embedding 模型生成 768 维向量,应根据实际模型修改。
3. 写入向量
sql
INSERT INTO document_chunk (
document_id,
content,
category,
embedding
) VALUES (
1001,
'Spring Cache 可以减少重复数据库查询',
'Java',
'[0.12, -0.31, 0.27]'
);
实际写入时,向量必须包含表结构声明的完整维度。
4. 余弦距离查询
sql
SELECT
id,
content,
embedding <=> CAST(? AS vector) AS distance
FROM document_chunk
WHERE category = 'Java'
ORDER BY embedding <=> CAST(? AS vector)
LIMIT 5;
在 pgvector 中,<=> 用于计算余弦距离,距离越小表示越接近。
5. 创建 HNSW 索引
sql
CREATE INDEX document_embedding_hnsw
ON document_chunk
USING hnsw (embedding vector_cosine_ops);
6. 创建 IVFFlat 索引
sql
CREATE INDEX document_embedding_ivfflat
ON document_chunk
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
近似索引会用部分召回率换取速度。索引创建完成后,返回结果可能与精确搜索略有区别。
十、Java 查询示例
可以使用 JDBC 执行向量查询:
java
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.stream.Collectors;
public class VectorSearchService {
private final Connection connection;
public VectorSearchService(Connection connection) {
this.connection = connection;
}
public void search(List<Double> queryVector) throws Exception {
String vectorText = queryVector.stream()
.map(String::valueOf)
.collect(Collectors.joining(",", "[", "]"));
String sql = """
SELECT id,
content,
embedding <=> CAST(? AS vector) AS distance
FROM document_chunk
WHERE category = ?
ORDER BY embedding <=> CAST(? AS vector)
LIMIT ?
""";
try (PreparedStatement statement =
connection.prepareStatement(sql)) {
statement.setString(1, vectorText);
statement.setString(2, "Java");
statement.setString(3, vectorText);
statement.setInt(4, 5);
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
System.out.printf(
"id=%d, distance=%f, content=%s%n",
resultSet.getLong("id"),
resultSet.getDouble("distance"),
resultSet.getString("content")
);
}
}
}
}
}
生产项目中还需要考虑:
- 数据库连接池;
- 查询超时;
- 批量写入;
- 异常重试;
- Embedding 缓存;
- 权限过滤;
- 结果去重;
- 监控与日志。
十一、向量数据库在 RAG 中的作用
RAG 全称 Retrieval-Augmented Generation,即检索增强生成。
它的基本思路是:在调用大语言模型之前,先从知识库中检索相关资料,再把资料和问题一起交给模型。
text
原始文档
↓
文档解析与分段
↓
生成 Embedding
↓
写入向量数据库
用户问题
↓
生成查询向量
↓
Top-K 向量检索
↓
重排序与过滤
↓
相关上下文 + 用户问题
↓
大语言模型生成答案
向量数据库在 RAG 中负责"召回",但它并不负责判断答案是否真实,也不会自动消除模型幻觉。
一个高质量 RAG 系统还需要:
- 合理的文档分段;
- 关键词与向量混合检索;
- Metadata 过滤;
- Rerank 重排序;
- 引用来源;
- 权限控制;
- 召回效果评估;
- 答案忠实度评估。
十二、文档应该如何分段
如果直接把整本手册生成一个向量,检索结果通常过于宽泛;如果分段太小,又可能丢失上下文。
常见分段策略包括:
1. 固定长度分段
按照固定字符数或 Token 数切分:
text
chunkSize = 500
overlap = 100
实现简单,但可能破坏段落语义。
2. 按标题和段落分段
根据 Markdown 标题、HTML 标签或自然段切分,可以更好地保留语义结构。
3. 递归分段
依次尝试按照:
text
章节 → 段落 → 句子 → 字符
进行拆分,直到片段满足长度要求。
4. 重叠窗口
相邻片段保留一部分重叠内容,可以减少切分边界造成的信息丢失,但会增加存储量和重复召回。
十三、为什么需要混合检索
纯向量搜索擅长理解语义,但可能不擅长搜索:
- 商品编号;
- 错误码;
- 类名和方法名;
- 精确的人名;
- 缩写;
- 较少见的专业术语。
例如:
text
ORA-00942
NullPointerException
SKU-20260920
这类内容往往更适合关键词检索。
因此,生产系统经常同时执行:
text
向量搜索 + BM25 关键词搜索
然后融合两种结果,再交给 Rerank 模型重排序。
Qdrant 官方文档也强调,稠密向量擅长理解上下文,但可能遗漏精确技术术语和唯一标识,因此可以结合语义检索与词法检索。Qdrant 检索概览
十四、Metadata 过滤的重要性
向量相似并不意味着结果一定满足业务条件。
例如企业知识库可能需要限制:
text
department = 用户所属部门
permission <= 用户权限
effectiveTime <= 当前时间
status = 已发布
一次检索可以表示为:
json
{
"vector": [0.12, -0.31, 0.27],
"topK": 10,
"filter": {
"department": "研发部",
"status": "published"
}
}
权限过滤必须在服务端或数据库查询阶段实施,不能只依赖前端隐藏结果。
十五、常见向量数据库选型
1. pgvector
适合已经使用 PostgreSQL,希望同时保留事务、SQL 和向量检索能力的项目。
优点:
- 学习成本较低;
- 可以使用 SQL;
- 便于与现有业务数据关联;
- 运维组件较少。
2. Milvus
适合向量数据规模较大、检索吞吐量较高、需要多种索引策略的场景。
3. Qdrant
以向量搜索为核心,支持 Payload 过滤、稠密向量、稀疏向量及混合检索。
4. Elasticsearch
适合已经建设全文检索平台,同时需要增加向量搜索和混合检索能力的团队。
5. 托管向量数据库
适合希望减少部署和运维工作的项目,但需要评估:
- 服务费用;
- 网络延迟;
- 数据合规;
- 厂商锁定;
- 数据迁移成本。
十六、向量数据库选型指标
选择产品时,不应只比较单次查询速度,还需要关注:
- 向量数量和维度;
- 写入速度和更新频率;
- 查询并发量;
- P95、P99 查询延迟;
- 召回率;
- Metadata 过滤能力;
- 混合检索能力;
- 多租户和权限隔离;
- 扩容与备份能力;
- SDK 和生态;
- 部署与维护成本;
- 数据安全与合规要求。
向量检索不存在脱离场景的"最快数据库"。合理的做法是使用真实业务数据进行基准测试。
十七、项目实践建议
1. 保存原始文本
不要只保存向量,还要保存原始内容、来源和文档编号。
2. 记录模型版本
Embedding 模型升级后,旧向量和新向量可能不在同一个语义空间。
3. 先做小规模精确搜索基线
精确搜索可以作为评估近似索引召回率的基准。
4. 不要只关注 Top-1
RAG 更关心正确内容是否出现在 Top-K 结果中。
5. 建立离线评估集
准备"问题---相关文档"测试集,持续评估:
- Recall@K;
- Precision@K;
- MRR;
- NDCG;
- 查询延迟。
6. 对检索结果进行重排序
向量搜索负责快速召回候选,Rerank 模型负责更精确地判断相关性。
7. 做好数据更新与删除
原文被修改或删除时,应同步更新向量和索引,避免召回过期内容。
8. 监控索引参数
提高 HNSW 的搜索候选数量可能提升召回率,但会增加延迟。参数调优应以实际评估数据为依据。
十八、常见误区
误区一:向量数据库可以代替关系型数据库
向量数据库擅长相似度检索,但订单事务、财务统计、精确关联查询仍然更适合关系型数据库。
误区二:换一个向量数据库就能提高 RAG 准确率
RAG 效果还受到以下因素影响:
- Embedding 模型;
- 文档质量;
- 分段策略;
- 查询改写;
- 混合检索;
- Rerank;
- Prompt;
- 大语言模型能力。
误区三:相似度最高的结果一定正确
相似度只表示在特定向量空间中的接近程度,并不代表事实正确。
误区四:Top-K 越大越好
Top-K 太小可能漏掉正确文档,太大则可能引入噪声并占用更多模型上下文。
误区五:建立索引后结果必须完全一致
近似索引会使用部分召回率换取速度,因此结果可能与精确搜索不同。pgvector 官方文档也明确说明,添加近似索引后查询结果可能发生变化。pgvector 索引说明
十九、总结
向量数据库的核心能力,是根据向量之间的距离查找语义相似的数据。
学习和使用向量数据库,可以按照以下路线推进:
- 理解 Embedding 和向量空间;
- 掌握余弦相似度、L2 和内积;
- 理解精确搜索与近似搜索的区别;
- 熟悉 HNSW、IVFFlat 等索引;
- 学会将向量与 Metadata 一起存储;
- 使用 pgvector 或专业向量数据库完成检索;
- 在 RAG 中加入分段、过滤、混合检索和重排序;
- 建立召回率、准确率和延迟评估体系。
向量数据库不是大语言模型的附属组件,而是一种通用的相似度检索基础设施。只要业务问题涉及"寻找与某个对象最相似的内容",就值得考虑使用向量检索。