Skip to Content
全部文章全栈、LLM向量数据库及应用场景

向量数据库及应用场景

发布时间: 2026-06-22

前言

最近顺应时代潮流,又转回全栈开发了,开始学习一些后端的内容,感觉在ai时代,死磕一些具体的语法的收益比较小并且不可取了,要往工程化、架构能力上走,知识的广度更重要一些了。

挑一些感兴趣的内容学习一些基础知识,看了一些向量数据库的内容,整理一篇笔记文章吧。

什么是向量,什么是向量数据库

向量 就是一组数字,计算机用来表示某个事物的 位置坐标

根据不同的维度向量,他们通常可以是这样的:

  • 一维向量 [5],表示一维空间中的一个点,比如x轴上的一个位置
  • 二维向量 [4,5],表示二维空间中的一个点,比如x、y轴中的一个位置
  • 三维向量 [4,5,6],表示三维空间中的一个点,比如三维坐标系中的一个位置
  • 高维向量 [4,…,x],表示高维空间中的一个点,这个维度通常达到几百、几千,可以表示的内容就非常丰富了

那什么是向量数据库呢?

将无数个经过同一 Embedding(嵌入) 流程处理后的得到的向量存储、管理起来的数据库就是向量数据库。

向量数据库和传统数据库的区别

向量数据库的最大不一样是,它通过将异构的数据(文字、图片、音频、视频)通过Embedding转换为向量后,就类似于提取了数据的特征,便于搜索匹配, 比如你搜索”猫咪”在传统数据库中是无法搜索到”猫猫”和”小猫”之类的相近词的。

而向量化后,猫咪和小猫这些关键词都会处于一个相近的状态,我们去搜索时可搜索到相近的坐标向量,比如搜索跟目标向量最贴近top5向量, 拿到top5向量后经过元数据对比得知匹配到了:猫咪,小猫,猫猫,喵星人,咪咪 等数据。

数据向量化及检索的过程

数据向量化的大致过程就是,对一个词汇或者一句话、甚至一段文字进行向量转换,存储到数据库,对用户输入检索的文字也转换为向量,通过特定算法去查找用户输入的 向量最相近的向量存放在哪,读取对应向量的元信息来拿到转换为向量前的数据。

这个过程在现在的LLM中的RAG应用非常普及,事实上只要是非结构化,模糊化的数据检索都适合使用向量数据库。

下面以RAG检索来说说向量数据库的入库及检索流程。

从文本到向量

首先是文本的处理,大于大段文本处理,通常会经过如下几个阶段:

  1. 文档提取及解析,对纯文本、pdf、doc、图片等文件使用特定的工具提取内容,图片可能通过大模型识别内容转换为文字。
  2. 清洗和预处理,去除无效字符、脱敏隐私信息等
  3. 文本切块或分块,核心逻辑,有固定分块(500字符,可能存在语义被硬切断)、重叠分块(每个分块保留前一个分块和后一个分块的一部分内容,保留语义完整)等
  4. 向量化,把切好的分块送到模型中(如 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.pdfPage 3
uuid_v1_002[0.015, -0.210, 0.875, ..., 0.041]“差旅交通费报销时,需提供正规发票及行程单…”company_travel_policy.pdfPage 3
uuid_v2_001[-0.145, 0.562, -0.012, ..., 0.912]“研发部转正考核标准:需独立交付至少一个微服务模块…”hr_考核制度_2026.docxPage 12
uuid_v2_002[0.781, 0.023, -0.341, ..., -0.115]“代码提交规范:采用 Monorepo 架构的分支需遵循 Git Flow…”hr_考核制度_2026.docxPage 15

向量转换与检索算法

向量数据库怎么判断你搜索的数据在数据库中有没有匹配的数据?

用户输入”去北京办公的话,晚上住酒店一晚最多能报多少钱?“,系统会把你的这句话Embedding转化为一串高维向量,也会得到一串[xxxx,xxx,…,xxxx]的向量, 这里的Embedding模型必须要和向量表原始数据使用的一样,只有使用相同的转换模型才有可能匹配上。

得到用户输入的向量后,向量数据库开始计算。虽然你的问题里提到了“北京、住酒店、一晚、多少钱”,而数据库里没有任何一条记录包含这些词,但模型知道“住酒店”和“住宿”是同义词,“北京”属于“一二线城市”。

最终匹配召回的结果:数据库会判定 uuid_v1_001 的向量与你这句话的向量距离最近(最相似)。

注意力机制粗略了解

注意力机制是向量化的重要步骤,他让Embedding和传统的文字匹配和全文搜索有质的区别。

“我想吃苹果,因为我口渴了。” 和 “我想换新苹果,因为我手机坏了。” 这两个句子都有“苹果”这两个字。如果是传统的机器,它分不清这两个苹果有什么区别。而注意力机制在处理到“苹果”这个词时,会让它环顾四周去寻找线索:

第一句里,“苹果”发现后面不远处有一个词叫“口渴”。注意力机制就会把“苹果”和“口渴”强行连线(分配极高的权重),从而让机器明白:这里的苹果是一种水果。

第二句里,“苹果”发现了后面的“手机”和“坏了”。注意力机制瞬间把它们绑在一起,让机器明白:这里的苹果是一个电子产品品牌。

向量化

用上面的例子,实际上用户输入的”去北京办公的话,晚上住酒店一晚最多能报多少钱”和对dpf文档向量化的”因公出差的住宿标准为:一二线城市每日不超过500元…”他们的向量化流程举例:

假设模型的Embedding有2个维度”差旅/商务维度”、“地域级别维度”

实际openai的模型会有1536个维度来标志一段文本的向量结果,为了简化理解举例只有2个维度,所以需要你先理解再延伸到高纬度去理解怎么计算的匹配

假如我们的模型会给文本数据从2个维度去打分

  1. X轴(差旅报销度): 分数越高,越说明在聊公事、报销、钱;
  2. Y轴(城市消费级别): 分数越高,说明城市越大、消费水平越高。
城市级别 (Y) ^ 1.0| | | | | | | | 0.0+-------------------------------------> 差旅报销 (X) 0 1.0
  1. 扫描pdf的文字块: “因公出差的住宿标准为:一二线城市每日不超过500元…”

大模型的向量化逻辑: 提到了“因公出差、住宿标准、500元”,这是百分之百的差旅报销(X轴给 0.95),提到了“一二线城市”,代表高消费区域(Y轴给 0.85)。

步骤是:分词->编码->注意力机制。

例如分词为:[因公], [出差], [的], [住宿], [标准], [为], [:], [一二线], [城市], [每日], [不超过], [500], [元]

每一个Token会对应词表里的一个数字ID,举例: “因公”→1024,“出差”→3056,“住宿”→8812…

然后通过注意力机制,把每个token拿去和其他的token进行相关性比对,计算出权重,吸收有关联的词的特征,这就和传统匹配文字以及全文检索有质的区别和优势。

在海量文本中,“一二线城市”经常和“北京、上海、高消费”一起出现。 注意力机制在处理“一二线城市”时,会把它和前文的 “因公出差”、后文的 “500元” 强行绑定。 模型的特征网络会激活这样一个逻辑:一二线城市 = 消费级别极高的城市群。

得到向量坐标 A: [0.95,0.85]

  1. 用户提问: “去北京办公的话,晚上住酒店一晚最多能报多少钱”

大模型的理解: 提到了“办公、住酒店、报多少钱”,显然也是在聊差旅报销(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 分层导航

原理就像瓦片底图一样,一开始看到的是大块的,放大后看到更详细的,一层一层越来越细。比如我要找成都,先找到中国放大后,找到西南地区,再放大找四川省,再放大找成都市。贪心算法,始终找当前层最接近的一个子层,深入,重复贪心算法。

向量数据库的应用场景

上面说了向量数据库的匹配原理,在哪些场景需要用向量数据库?

  1. 知识库与智能客服,就像上面举例的场景,检索到匹配用户提问的内容后,rag提交给大模型回答,增强回答的质量。
  2. 以图搜图,对图形识别提取关键词,然后搜索类似的图
  3. 个性化推荐,对用户的访问数据向量化,匹配相似度高的推荐。
  4. 生物特征比对,人脸、指纹等。

可以总结一下,所有没办法精确匹配,模糊定义的场景,都应该使用向量数据库。

实际测试

向量数据库有多种,专业型向量数据库以及传统数据库的向量支持,现在是学习因此我直接在我的服务器pgsql上安装了向量插件 pgvector ,来测试效果。Embedding模型我选择了一个开源的中文语言嵌入模型 BAAI/bge-small-zh-v1.5, 下面展示pgsql中如何向量化、检索。

  1. 创建带向量字段的表+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()
  1. 插入数据和搜索数据
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()
  1. 入口函数,插入一些测试数据,然后等待用户输入内容来检索
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数据。

运行效果:

1

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

最后编辑于

hi