向量数据库:从核心原理到 RAG 与 Java 实战

随着大语言模型、语义搜索和智能推荐的发展,传统关键词检索已经难以满足"理解语义"的需求。向量数据库通过保存和检索 Embedding,让计算机可以根据内容的含义查找相似数据。

文章目录

一、什么是向量数据库

向量数据库是一类专门用于存储、索引和检索高维向量的数据库。

文本、图片、音频等非结构化数据经过 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 的多层图结构,以及 MefConstruction 和查询参数 ef 对性能的影响。Milvus HNSW 文档

常见参数包括:

  • M:每个节点允许连接的邻居数量;
  • efConstruction:构建索引时考虑的候选节点数量;
  • efSearchef:查询时参与计算的候选数量。

参数越大,召回率通常越高,但构建时间、查询成本和内存消耗也会增加。

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. 托管向量数据库

适合希望减少部署和运维工作的项目,但需要评估:

  • 服务费用;
  • 网络延迟;
  • 数据合规;
  • 厂商锁定;
  • 数据迁移成本。

十六、向量数据库选型指标

选择产品时,不应只比较单次查询速度,还需要关注:

  1. 向量数量和维度;
  2. 写入速度和更新频率;
  3. 查询并发量;
  4. P95、P99 查询延迟;
  5. 召回率;
  6. Metadata 过滤能力;
  7. 混合检索能力;
  8. 多租户和权限隔离;
  9. 扩容与备份能力;
  10. SDK 和生态;
  11. 部署与维护成本;
  12. 数据安全与合规要求。

向量检索不存在脱离场景的"最快数据库"。合理的做法是使用真实业务数据进行基准测试。

十七、项目实践建议

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 索引说明

十九、总结

向量数据库的核心能力,是根据向量之间的距离查找语义相似的数据。

学习和使用向量数据库,可以按照以下路线推进:

  1. 理解 Embedding 和向量空间;
  2. 掌握余弦相似度、L2 和内积;
  3. 理解精确搜索与近似搜索的区别;
  4. 熟悉 HNSW、IVFFlat 等索引;
  5. 学会将向量与 Metadata 一起存储;
  6. 使用 pgvector 或专业向量数据库完成检索;
  7. 在 RAG 中加入分段、过滤、混合检索和重排序;
  8. 建立召回率、准确率和延迟评估体系。

向量数据库不是大语言模型的附属组件,而是一种通用的相似度检索基础设施。只要业务问题涉及"寻找与某个对象最相似的内容",就值得考虑使用向量检索。

相关推荐
这个DBA有点耶8 小时前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
DBA_G8 小时前
从地面到云霄:GBase数据库在民航三大场景的落地实践
数据库
自由能燃气设备9 小时前
商用全预混低氮冷凝锅炉免费方案vs付费方案对比+选型避坑指南
大数据·数据库·人工智能
科创致远9 小时前
科创致远 ESOP 系统核心效能与实战价值展示
大数据·数据库·人工智能·精益工程
2601_9622186110 小时前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法
张洛闻Eren11 小时前
k8s云原生【第十课】:水平 Pod 自动扩缩容
运维·数据库·云原生·kubernetes·github
于平安11 小时前
MySQL-触发器
数据库·mysql
白远山11 小时前
上海24小时自助健身房解决方案实战指南与经验分享
java·数据库·架构·需求分析
Omics Pro11 小时前
斯坦福Nature+Science|广义虚拟细胞基础大模型
数据库·人工智能·算法·机器学习·自然语言处理
2603_9651481112 小时前
AI+API选品:下一代智能商务助手雏形已现
java·大数据·数据库·人工智能·数据挖掘