向量数据库及应用场景
发布时间: 2026-06-22
前言
最近顺应时代潮流,又转回全栈开发了,开始学习一些后端的内容,感觉在ai时代,死磕一些具体的语法的收益比较小并且不可取了,要往工程化、架构能力上走,知识的广度更重要一些了。
挑一些感兴趣的内容学习一些基础知识,看了一些向量数据库的内容,整理一篇笔记文章吧。
什么是向量,什么是向量数据库
向量 就是一组数字,计算机用来表示某个事物的 位置 或 坐标。
根据不同的维度向量,他们通常可以是这样的:
- 一维向量 [5],表示一维空间中的一个点,比如x轴上的一个位置
- 二维向量 [4,5],表示二维空间中的一个点,比如x、y轴中的一个位置
- 三维向量 [4,5,6],表示三维空间中的一个点,比如三维坐标系中的一个位置
- 高维向量 [4,…,x],表示高维空间中的一个点,这个维度通常达到几百、几千,可以表示的内容就非常丰富了
那什么是向量数据库呢?
将无数个经过同一 Embedding(嵌入) 流程处理后的得到的向量存储、管理起来的数据库就是向量数据库。
向量数据库和传统数据库的区别
向量数据库的最大不一样是,它通过将异构的数据(文字、图片、音频、视频)通过Embedding转换为向量后,就类似于提取了数据的特征,便于搜索匹配, 比如你搜索”猫咪”在传统数据库中是无法搜索到”猫猫”和”小猫”之类的相近词的。
而向量化后,猫咪和小猫这些关键词都会处于一个相近的状态,我们去搜索时可搜索到相近的坐标向量,比如搜索跟目标向量最贴近top5向量, 拿到top5向量后经过元数据对比得知匹配到了:猫咪,小猫,猫猫,喵星人,咪咪 等数据。
数据向量化及检索的过程
数据向量化的大致过程就是,对一个词汇或者一句话、甚至一段文字进行向量转换,存储到数据库,对用户输入检索的文字也转换为向量,通过特定算法去查找用户输入的 向量最相近的向量存放在哪,读取对应向量的元信息来拿到转换为向量前的数据。
这个过程在现在的LLM中的RAG应用非常普及,事实上只要是非结构化,模糊化的数据检索都适合使用向量数据库。
下面以RAG检索来说说向量数据库的入库及检索流程。
从文本到向量
首先是文本的处理,大于大段文本处理,通常会经过如下几个阶段:
- 文档提取及解析,对纯文本、pdf、doc、图片等文件使用特定的工具提取内容,图片可能通过大模型识别内容转换为文字。
- 清洗和预处理,去除无效字符、脱敏隐私信息等
- 文本切块或分块,核心逻辑,有固定分块(500字符,可能存在语义被硬切断)、重叠分块(每个分块保留前一个分块和后一个分块的一部分内容,保留语义完整)等
- 向量化,把切好的分块送到模型中(如 OpenAI 的 text-embedding-3-small、Hugging Face 上的开源模型等),模型理解其语法、语境、含义后提取特征,输出一个固定长度的高维向量数组。
经过这个处理流程,我们输入一个pdf文档,最终可能得到多个高维数组(每个分块产出一个)。
向量数据的形态
如果我们有10个pdf,就会得到每个pdf的分块和的高维向量数组,结构示意表:
| 唯一标识 (ID) | 向量数据 (Vector) | 原始文本内容 (Scalar/Payload) | 元数据 | 元数据 |
|---|---|---|---|---|
uuid_v1_001 | [0.012, -0.234, 0.891, ..., 0.056] | “因公出差的住宿标准为:一二线城市每日不超过500元…” | company_travel_policy.pdf | Page 3 |
uuid_v1_002 | [0.015, -0.210, 0.875, ..., 0.041] | “差旅交通费报销时,需提供正规发票及行程单…” | company_travel_policy.pdf | Page 3 |
uuid_v2_001 | [-0.145, 0.562, -0.012, ..., 0.912] | “研发部转正考核标准:需独立交付至少一个微服务模块…” | hr_考核制度_2026.docx | Page 12 |
uuid_v2_002 | [0.781, 0.023, -0.341, ..., -0.115] | “代码提交规范:采用 Monorepo 架构的分支需遵循 Git Flow…” | hr_考核制度_2026.docx | Page 15 |
向量转换与检索算法
向量数据库怎么判断你搜索的数据在数据库中有没有匹配的数据?
用户输入”去北京办公的话,晚上住酒店一晚最多能报多少钱?“,系统会把你的这句话Embedding转化为一串高维向量,也会得到一串[xxxx,xxx,…,xxxx]的向量, 这里的Embedding模型必须要和向量表原始数据使用的一样,只有使用相同的转换模型才有可能匹配上。
得到用户输入的向量后,向量数据库开始计算。虽然你的问题里提到了“北京、住酒店、一晚、多少钱”,而数据库里没有任何一条记录包含这些词,但模型知道“住酒店”和“住宿”是同义词,“北京”属于“一二线城市”。
最终匹配召回的结果:数据库会判定 uuid_v1_001 的向量与你这句话的向量距离最近(最相似)。
注意力机制粗略了解
注意力机制是向量化的重要步骤,他让Embedding和传统的文字匹配和全文搜索有质的区别。
“我想吃苹果,因为我口渴了。” 和 “我想换新苹果,因为我手机坏了。” 这两个句子都有“苹果”这两个字。如果是传统的机器,它分不清这两个苹果有什么区别。而注意力机制在处理到“苹果”这个词时,会让它环顾四周去寻找线索:
第一句里,“苹果”发现后面不远处有一个词叫“口渴”。注意力机制就会把“苹果”和“口渴”强行连线(分配极高的权重),从而让机器明白:这里的苹果是一种水果。
第二句里,“苹果”发现了后面的“手机”和“坏了”。注意力机制瞬间把它们绑在一起,让机器明白:这里的苹果是一个电子产品品牌。
向量化
用上面的例子,实际上用户输入的”去北京办公的话,晚上住酒店一晚最多能报多少钱”和对dpf文档向量化的”因公出差的住宿标准为:一二线城市每日不超过500元…”他们的向量化流程举例:
假设模型的Embedding有2个维度”差旅/商务维度”、“地域级别维度”
实际openai的模型会有1536个维度来标志一段文本的向量结果,为了简化理解举例只有2个维度,所以需要你先理解再延伸到高纬度去理解怎么计算的匹配
假如我们的模型会给文本数据从2个维度去打分
- X轴(差旅报销度): 分数越高,越说明在聊公事、报销、钱;
- Y轴(城市消费级别): 分数越高,说明城市越大、消费水平越高。
城市级别 (Y)
^
1.0|
|
|
|
|
|
|
|
0.0+-------------------------------------> 差旅报销 (X)
0 1.0- 扫描pdf的文字块: “因公出差的住宿标准为:一二线城市每日不超过500元…”
大模型的向量化逻辑: 提到了“因公出差、住宿标准、500元”,这是百分之百的差旅报销(X轴给 0.95),提到了“一二线城市”,代表高消费区域(Y轴给 0.85)。
步骤是:分词->编码->注意力机制。
例如分词为:[因公], [出差], [的], [住宿], [标准], [为], [:], [一二线], [城市], [每日], [不超过], [500], [元]
每一个Token会对应词表里的一个数字ID,举例: “因公”→1024,“出差”→3056,“住宿”→8812…
然后通过注意力机制,把每个token拿去和其他的token进行相关性比对,计算出权重,吸收有关联的词的特征,这就和传统匹配文字以及全文检索有质的区别和优势。
在海量文本中,“一二线城市”经常和“北京、上海、高消费”一起出现。 注意力机制在处理“一二线城市”时,会把它和前文的 “因公出差”、后文的 “500元” 强行绑定。 模型的特征网络会激活这样一个逻辑:一二线城市 = 消费级别极高的城市群。
得到向量坐标 A: [0.95,0.85]
- 用户提问: “去北京办公的话,晚上住酒店一晚最多能报多少钱”
大模型的理解: 提到了“办公、住酒店、报多少钱”,显然也是在聊差旅报销(X轴给 0.90)。提到了“北京”,北京是一线城市、消费极高(Y轴给 0.95)。
向量化步骤同上,步骤是:分词->编码->注意力机制。
得到向量坐标 B: [0.90,0.95]
他们在坐标上就是:
城市级别 (Y)
^
1.0| * 查询 B [0.90, 0.95] (去北京住酒店能报多少)
| /.
| / .
| / * 文本 A [0.95, 0.85] (一二线城市不超过500)
| / /
| / /
| / /
| / /
0.0+-------------------------------------> 差旅报销 (X)
0 1.0但在上面的二维图里,从原点发出的两条线,几乎是朝着同一个方向并排前进的,它们之间的夹角非常非常小,当夹角很小时可以认为他们非常相似,这就是向量数据库常见的 余弦相似度 匹配算法。
还有 点积(对向量归一化,每个向量的长度都被缩放到1之后,对比速度极快,相似度和余弦方式计算是一致的) 和 欧氏距离 (直接量两点之间的直线距离,越小越相似);
这个例子是2维度的情况下的示例,你可以想象1536个维度的时候,这个向量矩阵只是维度变得复杂,其他逻辑都是一模一样的。
向量数据库的索引
暴力检索
就是一条一条的遍历找到最匹配的数据,结果准确,速度慢如何在海量数据中找到相似数据不能用这种方式。
IVF 倒排文件索引
原理就是,将向量聚合成一个个簇,记录每个簇的中心,然后查找的时候找最接近的中心,找到再在这些簇中进行检索。
HNSW 分层导航
原理就像瓦片底图一样,一开始看到的是大块的,放大后看到更详细的,一层一层越来越细。比如我要找成都,先找到中国放大后,找到西南地区,再放大找四川省,再放大找成都市。贪心算法,始终找当前层最接近的一个子层,深入,重复贪心算法。
向量数据库的应用场景
上面说了向量数据库的匹配原理,在哪些场景需要用向量数据库?
- 知识库与智能客服,就像上面举例的场景,检索到匹配用户提问的内容后,rag提交给大模型回答,增强回答的质量。
- 以图搜图,对图形识别提取关键词,然后搜索类似的图
- 个性化推荐,对用户的访问数据向量化,匹配相似度高的推荐。
- 生物特征比对,人脸、指纹等。
可以总结一下,所有没办法精确匹配,模糊定义的场景,都应该使用向量数据库。
实际测试
向量数据库有多种,专业型向量数据库以及传统数据库的向量支持,现在是学习因此我直接在我的服务器pgsql上安装了向量插件 pgvector ,来测试效果。Embedding模型我选择了一个开源的中文语言嵌入模型 BAAI/bge-small-zh-v1.5,
下面展示pgsql中如何向量化、检索。
- 创建带向量字段的表+hnsw分层导航的索引
def setup_schema(conn) -> None:
with conn.cursor() as cur:
# pgvector 不是一个独立服务,而是 PostgreSQL 扩展。
# 开启扩展后,PostgreSQL 就多了 vector 类型、向量距离运算符和向量索引能力。
cur.execute("CREATE EXTENSION IF NOT EXISTS vector")
cur.execute(
"""
CREATE TABLE IF NOT EXISTS documents (
id bigserial PRIMARY KEY,
title text NOT NULL,
content text NOT NULL,
-- embedding 保存 bge-small-zh-v1.5 生成的 512 维语义向量。
-- 这列相当于“可被相似度搜索的语义特征”。
embedding vector(512) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
)
"""
)
cur.execute(
"""
-- HNSW 是近似最近邻索引,用来加速 topK 向量检索。
-- vector_cosine_ops 表示按余弦距离进行向量相似度搜索。
CREATE INDEX IF NOT EXISTS documents_embedding_hnsw_idx
ON documents USING hnsw (embedding vector_cosine_ops)
"""
)
conn.commit()
- 插入数据和搜索数据
def insert_documents(
conn,
model: SentenceTransformer,
documents: Iterable[DemoDocument],
) -> list[int]:
ids: list[int] = []
with conn.cursor() as cur:
for doc in documents:
# 这里用 title + content 生成 embedding,所以检索时二者都会影响语义匹配。
# 如果只想检索正文,可以改成 encode(model, doc.content)。
embedding = encode(model, f"{doc.title}\n{doc.content}")
cur.execute(
"""
INSERT INTO documents (title, content, embedding)
VALUES (%s, %s, %s)
RETURNING id
""",
(doc.title, doc.content, embedding),
)
ids.append(cur.fetchone()[0])
conn.commit()
return ids
def search(conn, model: SentenceTransformer, query: str, limit: int = 3) -> list[tuple]:
# 用户输入的查询句子也会先转换成向量,再和数据库里的 embedding 做距离计算。
query_embedding = encode(model, query)
with conn.cursor() as cur:
cur.execute(
"""
SELECT
id,
title,
content,
-- <=> 是 pgvector 的余弦距离运算符,距离越小越相似。
-- 因为前面做了 normalize_embeddings=True,这里用 1 - distance 近似展示相似度。
round((1 - (embedding <=> %s::vector))::numeric, 4) AS cosine_similarity
FROM documents
-- 这就是向量检索的核心:按“文档向量”和“查询向量”的距离排序。
-- 写法仍然是 SQL,因为 pgvector 把向量能力集成进了 PostgreSQL。
ORDER BY embedding <=> %s::vector
LIMIT %s
""",
(query_embedding, query_embedding, limit),
)
return cur.fetchall()- 入口函数,插入一些测试数据,然后等待用户输入内容来检索
def main() -> None:
model_name = os.getenv("MODEL_NAME", "BAAI/bge-small-zh-v1.5")
print(f"Loading embedding model: {model_name}")
model = SentenceTransformer(model_name)
with connect(**db_config()) as conn:
register_vector(conn)
setup_schema(conn)
docs = [
DemoDocument("iPhone 15 Pro 性能", "A17 Pro 芯片适合运行大型手游、视频剪辑和高负载应用。"),
DemoDocument("iPhone 15 拍照", "4800 万像素主摄适合日常拍照,能够保留更多画面细节。"),
DemoDocument("iPhone 电池续航", "想要长续航可以关注 Plus 或 Pro Max 机型,它们通常电池容量更大。"),
DemoDocument("苹果手机系统", "iOS 系统更新周期长,应用生态稳定,适合重视长期使用体验的用户。"),
DemoDocument("Face ID 解锁", "苹果手机使用 Face ID 进行面部识别,暗光环境下也能快速解锁。"),
DemoDocument("MagSafe 配件", "MagSafe 可以吸附无线充电器、磁吸支架、卡包和移动电源。"),
DemoDocument("iPhone 存储容量", "经常拍视频或下载大型应用时,256GB 比 128GB 更从容。"),
DemoDocument("二手 iPhone 选购", "购买二手苹果手机时要检查电池健康、屏幕状态、序列号和是否有隐藏锁。"),
DemoDocument("iPhone 与 Mac 协同", "苹果手机可以和 Mac 使用隔空投送、接力、短信同步和通用剪贴板。"),
DemoDocument("USB-C 接口", "新款 iPhone 使用 USB-C 接口,充电线和部分数码设备更容易通用。"),
]
if count_documents(conn) == 0:
ids = insert_documents(conn, model, docs)
print(f"\n数据库为空,已插入样例 document ids: {ids}")
print_documents(list_documents(conn))
interactive_search(conn, model)因为它是pgsql的一个插件,因此在使用上还是很贴近传统的sql语句的,看起来就是多了一个向量类型的字段可以用来存向量,检索也就是按照相近度倒排序来取前面的top数据。
运行效果:

第一个问题,没有回答出来,也是意料之中,因为它的原理就是匹配相近度,而没有逻辑推理的能力,后面两个问题都回答得不错。至此向量数据库在心中有一个概念了。
