向量数据库入门:SQL 数据库做不了的"按意思找内容"
SQL 能查"等于、包含、范围",但查不了"意思接近"。文本 → embedding → ANN 索引 → 查询,向量数据库就是为这条路径建造的。
做过几年 web 开发的人都写过各种各样的搜索功能。WHERE name = 'iPhone'、WHERE description LIKE '%手机%'、PostgreSQL 的全文索引 to_tsvector @@ to_tsquery,套路都很熟。
但接到一个新需求会让人愣住:在产品手册里搜"关于退货政策的内容"。文档里写的是"七天无理由换货"。LIKE '%退货%' 找不到——文档里没有这两个字。全文索引也找不到——"退货"和"换货"在索引器看来是不同的词。
这个场景里的"找",需要的不是字符层面的匹配,而是意思层面的匹配。SQL 这套体系不是为此设计的。向量数据库是。
后面是这件事的完整路径——一段文本怎么变成向量、向量之间的距离怎么算、为什么要专门搞个数据库、以及一次完整查询长什么样。
为什么 SQL 数据库做不了"按意思找"
关系型数据库的查询语义无外乎精确匹配加范围筛选:
SELECT * FROM products WHERE price BETWEEN 100 AND 500;
SELECT * FROM orders WHERE status = 'paid';
SELECT * FROM users WHERE email = 'alice@example.com';
引擎做的事是看每一行的列值是否满足条件——=、<、>、IN、BETWEEN。
为字符串增加灵活性的方案有两种:
LIKE '%关键词%'—— 字符子串匹配- 全文索引
to_tsvector/MATCH AGAINST—— 词项级匹配,能处理词形变化和停用词
但这两种都还在"字面层级"工作。"退货"和"换货"是不同字符;"开通 SSO"和"启用单点登录"是不同词项。SQL 没有任何机制理解它们意思接近。
要做"按意思搜",先得解决一件事:如何把"意思"变成可以被比较的东西。
把"意思"变成可以比较的东西
Mikolov 等人 2013 年在 Word2Vec 论文里展示了一件让人印象深刻的事:把每个词训练成一个 300 维的浮点数组(向量),可以做出这样的等式:
vec("国王") - vec("男人") + vec("女人") ≈ vec("女王")
意思不再是抽象概念,而是高维空间里的一个坐标。意思相近的词,坐标也接近。
到了 2018 年的 BERT 和 2019 年的 Sentence-BERT,这件事扩展到整句话甚至整段文本——一段文本可以被映射成一个固定长度的向量,意思接近的两段文本在向量空间里也接近。这个把文本变成向量的过程叫 embedding。
今天用 OpenAI API 做这件事就一行代码:
from openai import OpenAI
client = OpenAI()
resp = client.embeddings.create(
model="text-embedding-3-small",
input="七天无理由换货"
)
vec = resp.data[0].embedding # 1536 个浮点数
vec 是一个长度 1536 的 float 数组。同样的代码对"退货政策"调一次,会得到另一个 1536 维的向量。两个向量在数值上不一样,但因为意思接近,它们在向量空间里"靠得很近"。
一个诚实的注脚:上面那个国王/女王等式是 Word2Vec 时代的演示,画图直觉很好。但 BERT 之后的现代 embedding 不一定能严格满足这种线性等式——它们更多是"意思接近时向量距离也接近"这种相邻关系。直觉留着,等式别太较真。
OpenAI text-embedding-3-small 在 2024 年 1 月发布,价格 0.02 美元每百万 token,便宜到几乎可以忽略;维度默认 1536,效果比 2022 年的 ada-002 全面提升。如果不想依赖闭源 API,BAAI 在 2023 年 8 月开源的 BGE 系列在 MTEB 评测榜上长期占据靠前位置,中英文都能用。
到这里你有了一组"意思的坐标"。下一步是怎么测量"接近"。
向量之间的距离怎么算
三种最常见的度量:
| 度量 | 直觉 | 适用 |
|---|---|---|
| 余弦相似度 | 看两个向量"指的方向"接不接近,忽略长度 | 文本 embedding 默认选择 |
| 欧氏距离(L2) | 看两个点在几何空间里的距离 | 图像、声音特征也常用 |
| 内积 / 点积 | 同方向相乘求和 | 计算最快 |
对文本来说一般选余弦相似度。原因是文本 embedding 关心的是"语义方向"——一段文字写得长还是短,不应该影响它的意思。
有个工程小细节值得记住:如果 embedding 模型输出的是归一化向量(长度 = 1),余弦相似度、L2 距离、内积三者排序结果完全一致。OpenAI 的 embedding 接口默认就归一化,所以选哪个度量其实差不多,挑一个数据库支持得最稳的就行。
数据库该存什么、查什么
最朴素的实现是这样:
- 把每个文档调 embedding API,得到一个向量
- 把"文档 ID + 文本 + 向量"存进数据库
- 用户提问时,把问题也调 embedding,得到查询向量
- 让查询向量和库里每个文档向量算余弦相似度
- 取相似度最高的 top-K 文档返回
用 PostgreSQL 加 pgvector 扩展演示这个流程:
CREATE EXTENSION vector;
CREATE TABLE docs (
id bigserial PRIMARY KEY,
content text,
embedding vector(1536)
);
-- 插入文档(embedding 在应用层算好)
INSERT INTO docs(content, embedding) VALUES
('七天无理由换货', '[0.12, -0.04, ...]'),
('支持 SAML SSO', '[0.08, 0.31, ...]');
-- 查询:找跟问题最相近的 5 条
SELECT id, content
FROM docs
ORDER BY embedding <=> '[0.10, -0.02, ...]'::vector
LIMIT 5;
<=> 是 pgvector 的余弦距离操作符(值越小越相近)。这条查询就完成了"按意思找"。pgvector 同样提供 <->(L2 距离)和 <#>(负内积),按需切换。
一个新问题马上冒出来:上面那条 ORDER BY 实际上是把库里每一条文档拿出来跟查询向量算一次距离。库里有 1000 条还好,有 100 万条呢?1536 维的浮点数组算一次余弦距离是几千次乘加,100 万条就是几十亿次。一次查询花上几秒,没法用。
这就是为什么需要专门的"向量数据库"——准确地说,需要一个近似最近邻索引。
向量数据库其实是个 ANN 索引
ANN 是 Approximate Nearest Neighbor 的缩写:近似最近邻。它的工程权衡是这样的——
- 精确最近邻(线性扫描全库):保证找到最优解,但慢
- ANN(建索引,按图或聚类跳着搜):不保证一定找到最优解,但能在几毫秒内给出"非常接近最优解"的答案
向量数据库做的最关键的事就是给百万级、千万级、亿级的向量建 ANN 索引。
主流 ANN 索引有两种,入门只需要理解第一种。
HNSW (Hierarchical Navigable Small World) 是 Malkov 和 Yashunin 2016 年的论文。直觉:把所有向量组织成一个分层的图——
- 顶层只有少量节点,节点之间是"长距离跳跃"的边(像高速公路)
- 越往下层节点越多,节点之间是"短距离精细"的边(像市区道路)
搜索时先从顶层一个入口节点出发,沿着边走到离查询向量更近的节点;当某层走不动了,下降一层;最底层走完时找到的就是 top-K 近似最近邻。
复杂度从线性扫描的 O(N) 降到 O(log N)。100 万条 1536 维向量的查询通常在 2 到 10 毫秒之间完成。代价是要多用一些内存——HNSW 索引大约要原始向量大小的 1.2 到 2 倍。
HNSW 有三个旋钮需要调:
M:每个节点的连边数,典型值 8 到 48。M 越大召回越准,内存越多。efConstruction:建索引时的候选列表大小,典型 100 到 500。越大索引质量越好、建得越慢。efSearch:查询时的候选列表大小,典型 50 到 500。越大召回越好、查询越慢。
调旋钮的本质是在"准 / 快 / 省内存"三件事里取折中。
另一种主流索引是 IVF (Inverted File Index)——先用 k-means 把向量分成若干个簇(nlist),查询时只搜跟查询向量最近的几个簇(nprobe)。原理更简单,配合 Product Quantization (PQ) 可以非常省内存,适合超大规模但能容忍更低召回的场景。入门可以先跳过。
一次完整查询的流程
把上面所有东西串起来。
图 1:一次向量数据库查询的完整流程
flowchart TD
subgraph 离线索引
D[原始文档] --> S[切分成片段]
S --> E1[调 Embedding API<br/>每段变成 1536 维向量]
E1 --> I[写入向量数据库<br/>建 HNSW 索引]
end
subgraph 在线查询
Q[用户问题] --> E2[调 Embedding API<br/>问题变成 1536 维向量]
E2 --> A[向量数据库 ANN 检索]
I -.被检索.-> A
A --> K[Top-K 最近邻片段]
K --> R[返回原文给应用层<br/>通常再交给 LLM 生成答案]
end
注意 embedding 这一步用的是同一个模型——离线索引时把文档转向量用 text-embedding-3-small,在线查询把问题转向量也必须用 text-embedding-3-small。换模型意味着所有向量都得重建。这是工程上一个常见坑。
主流方案速览
入门阶段不用记十个产品,按下面这条路径选就够:
| 场景 | 推荐 |
|---|---|
| 已有 PostgreSQL、文档百万级以下 | pgvector(PostgreSQL 扩展,2023 年 8 月起支持 HNSW) |
| 要全托管、不想运维 | Pinecone(2019 年创立,2021 年商用,行业里"默认选项") |
| 要开源、要功能丰富 | Milvus(CNCF 项目,HNSW / IVF / DiskANN 都有) |
| 要开源 + 强过滤 + Rust 性能 | Qdrant(Rust 写的,filter 支持很好) |
| Python 原型、本地玩 | Chroma(默认本地存储,LangChain / LlamaIndex 教程里最常见) |
| 要 hybrid(稀疏 + 稠密)一站搞定 | Weaviate(内置 BM25 + 向量混合检索) |
工程上的常见路径:原型阶段用 Chroma 跑通流程,业务上线时落到 pgvector(已有 PostgreSQL)或 Qdrant / Pinecone(专用方案),数据量真上去后再评估 Milvus。
常见疑问
Embedding 模型该选哪个? OpenAI text-embedding-3-small 在 2024 年初是性价比最高的入门选择,每百万 token 只要 0.02 美元,1536 维,效果稳定。如果不想依赖闭源 API,BGE-large 系列(中英文都有)在 MTEB 榜上靠前,本地部署也方便。
一段文本太长怎么办? Embedding 模型有 token 上限——text-embedding-3-small 是 8192,但实际很少有人把整篇长文当一个 chunk。常见做法是先切片(chunking),再对每片做 embedding。
新增文档怎么处理? 向量数据库都支持增量插入——新文档算 embedding 写一条进去就行。但如果你换了 embedding 模型,所有历史向量都需要重新算并替换,这是一个相对重的操作,需要做版本管理。
精度不够 / 召回不准怎么办? 按顺序排查:先看 chunking 切得对不对,再看 embedding 模型选得对不对,最后才是调 HNSW 的 efSearch。绝大多数"召回不准"的问题在前两步就能解决,不是 ANN 索引的锅。
一句话结尾
SQL 是为"等于、包含、范围"设计的;向量数据库是为"按意思找"设计的。把文本变成向量、用 ANN 索引在百万规模里毫秒级找到 top-K 近邻,三件事加起来就是这个新工具的全部基本盘。剩下的复杂度,都是从这三件事衍生出来的。
参考资料
- Mikolov et al., "Efficient Estimation of Word Representations in Vector Space," ICLR 2013. https://arxiv.org/abs/1301.3781
- Devlin et al., "BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding," 2018. https://arxiv.org/abs/1810.04805
- Reimers & Gurevych, "Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks," EMNLP 2019. https://arxiv.org/abs/1908.10084
- Malkov & Yashunin, "Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs," 2016. https://arxiv.org/abs/1603.09320
- OpenAI, "New embedding models and API updates," January 25, 2024. https://openai.com/index/new-embedding-models-and-api-updates/
- OpenAI, "New and improved embedding model," December 15, 2022. https://openai.com/index/new-and-improved-embedding-model/
- BAAI, "BGE: BAAI General Embedding," 2023. https://huggingface.co/BAAI/bge-large-en
- pgvector, GitHub repository. https://github.com/pgvector/pgvector
- Pinecone, "Vector Similarity Explained." https://www.pinecone.io/learn/vector-similarity/
- Faiss launch announcement, Engineering at Meta, March 29, 2017. https://engineering.fb.com/2017/03/29/data-infrastructure/faiss-a-library-for-efficient-similarity-search/
我是姚磊
大模型应用开发者,关注 RAG、Agent 和产品化工程。沉淀实践中的判断、踩坑记录和可复用方法。联系我:yaolei_agent@163.com



