向量数据库为什么火了?
2026年,如果你还没接触过向量数据库,那你一定错过了AI时代最重要的基础设施之一。从关键词搜索到语义搜索的范式转变,正在重塑整个搜索技术栈。
关键词搜索:用户搜"苹果",只能匹配包含"苹果"两个字的文档 语义搜索:用户搜"苹果",能理解你可能想找"iPhone"、"MacBook"或"水果营养"
这种能力的跃迁,核心在于Embedding(向量嵌入)——将文本转化为高维空间中的数值向量,语义相近的文本在向量空间中距离更近。
Embedding模型选择
选择合适的Embedding模型是语义搜索的基础。以下是2026年主流模型的对比:
| 模型 | 维度 | 最大Token | 多语言 | 开源 | MTEB排名 | 特点 |
|---|---|---|---|---|---|---|
| OpenAI text-embedding-3-large | 3072 | 8191 | ✅ | ❌ | Top 5 | 通用性强,API调用简单 |
| OpenAI text-embedding-3-small | 1536 | 8191 | ✅ | ❌ | Top 15 | 性价比高 |
| BGE-M3 | 1024 | 8192 | ✅ | ✅ | Top 10 | 多语言多粒度,支持稠密+稀疏+ColBERT |
| Cohere embed-v4 | 1024 | 128000 | ✅ | ❌ | Top 8 | 超长文本,多模态 |
| GTE-Qwen2 | 1536 | 32768 | ✅ | ✅ | Top 3 | 阿里开源,中文表现优异 |
| E5-Mistral-7B | 4096 | 32768 | ✅ | ✅ | Top 2 | 大模型嵌入,精度最高但推理慢 |
选择建议
追求精度 + 有GPU → GTE-Qwen2 / E5-Mistral-7B
追求性价比 + 快速上线 → OpenAI text-embedding-3-small
中文为主 + 需要开源 → BGE-M3 / GTE-Qwen2
超长文档 → Cohere embed-v4
多语言 + 混合检索 → BGE-M3(同时输出稠密+稀疏向量)
Embedding生成示例
@Service
public class EmbeddingService {
private final RestTemplate restTemplate;
@Value("${embedding.api.url}")
private String apiUrl;
@Value("${embedding.api.key}")
private String apiKey;
@Value("${embedding.model}")
private String model;
public float[] generateEmbedding(String text) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(apiKey);
Map<String, Object> body = Map.of(
"model", model,
"input", text
);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
ResponseEntity<Map> response = restTemplate.postForEntity(
apiUrl + "/embeddings", request, Map.class
);
List<Double> embedding = (List<Double>) ((Map) ((List) response.getBody().get("data")).get(0)).get("embedding");
float[] result = new float[embedding.size()];
for (int i = 0; i < embedding.size(); i++) {
result[i] = embedding.get(i).floatValue();
}
return result;
}
}
主流向量数据库对比
| 特性 | Milvus | pgvector | Qdrant | Weaviate | Chroma |
|---|---|---|---|---|---|
| 类型 | 专用向量数据库 | PostgreSQL扩展 | 专用向量数据库 | 专用向量数据库 | 轻量级嵌入式 |
| 语言 | Go/C++ | C | Rust | Go | Python |
| 索引类型 | HNSW, IVF_PQ, Flat, SCANN | HNSW, IVFFlat | HNSW | HNSW | HNSW |
| 水平扩展 | ✅ 原生支持 | ❌ 需Citus | ✅ 分片 | ✅ 分片 | ❌ |
| 混合搜索 | ✅ | ✅ (SQL) | ✅ | ✅ | ✅ |
| 过滤 | ✅ 标量过滤 | ✅ SQL WHERE | ✅ Payload过滤 | ✅ GraphQL | ✅ 元数据过滤 |
| 事务 | ❌ | ✅ ACID | ❌ | ❌ | ❌ |
| 运维复杂度 | 高 | 低 | 中 | 中 | 极低 |
| 适用场景 | 大规模生产 | 已有PG的项目 | 中等规模 | 全功能搜索 | 原型/小规模 |
如何选择?
已有PostgreSQL基础设施 → pgvector,零额外运维成本 千万级以上向量 → Milvus,原生分布式 中等规模 + Rust性能 → Qdrant 快速原型验证 → Chroma 需要GraphQL + 多模态 → Weaviate
Spring Boot + pgvector实战
pgvector是PostgreSQL的扩展,让你在已有数据库中原生支持向量搜索,无需引入新的基础设施。
1. 环境准备
-- 安装pgvector扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 创建文档表
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(500) NOT NULL,
content TEXT NOT NULL,
source VARCHAR(200),
embedding vector(1536),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建HNSW索引(推荐)
CREATE INDEX idx_documents_embedding ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
2. Spring Boot配置
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: postgres
password: secret
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
3. 实体定义
@Entity
@Table(name = "documents")
public class Document {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 500)
private String title;
@Column(nullable = false, columnDefinition = "TEXT")
private String content;
private String source;
@Column(columnDefinition = "vector(1536)")
private float[] embedding;
@Column(name = "created_at")
private LocalDateTime createdAt;
public Document() {}
public Document(String title, String content, String source, float[] embedding) {
this.title = title;
this.content = content;
this.source = source;
this.embedding = embedding;
this.createdAt = LocalDateTime.now();
}
}
4. 向量搜索Repository
@Repository
public class DocumentVectorRepository {
private final JdbcTemplate jdbcTemplate;
public DocumentVectorRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List<DocumentSearchResult> searchSimilar(float[] queryEmbedding, int limit) {
String vectorStr = Arrays.stream(queryEmbedding)
.mapToObj(f -> String.format("%.6f", f))
.collect(Collectors.joining(",", "[", "]"));
String sql = """
SELECT id, title, content, source,
1 - (embedding <=> ?::vector) AS similarity
FROM documents
WHERE embedding IS NOT NULL
ORDER BY embedding <=> ?::vector
LIMIT ?
""";
return jdbcTemplate.query(sql,
(rs, rowNum) -> new DocumentSearchResult(
rs.getLong("id"),
rs.getString("title"),
rs.getString("content"),
rs.getString("source"),
rs.getDouble("similarity")
),
vectorStr, vectorStr, limit
);
}
public List<DocumentSearchResult> searchWithFilter(
float[] queryEmbedding, String category, int limit) {
String vectorStr = Arrays.stream(queryEmbedding)
.mapToObj(f -> String.format("%.6f", f))
.collect(Collectors.joining(",", "[", "]"));
String sql = """
SELECT id, title, content, source,
1 - (embedding <=> ?::vector) AS similarity
FROM documents
WHERE embedding IS NOT NULL
AND source = ?
ORDER BY embedding <=> ?::vector
LIMIT ?
""";
return jdbcTemplate.query(sql,
(rs, rowNum) -> new DocumentSearchResult(
rs.getLong("id"),
rs.getString("title"),
rs.getString("content"),
rs.getString("source"),
rs.getDouble("similarity")
),
vectorStr, category, vectorStr, limit
);
}
}
5. 搜索服务
@Service
public class SemanticSearchService {
private final EmbeddingService embeddingService;
private final DocumentVectorRepository vectorRepository;
public List<DocumentSearchResult> search(String query, int limit) {
float[] queryEmbedding = embeddingService.generateEmbedding(query);
return vectorRepository.searchSimilar(queryEmbedding, limit);
}
public List<DocumentSearchResult> searchByCategory(String query, String category, int limit) {
float[] queryEmbedding = embeddingService.generateEmbedding(query);
return vectorRepository.searchWithFilter(queryEmbedding, category, limit);
}
}
索引类型:HNSW、IVF_PQ、Flat
| 索引类型 | 原理 | 查询速度 | 召回率 | 内存占用 | 构建速度 | 适用场景 |
|---|---|---|---|---|---|---|
| Flat | 暴力搜索 | 慢 | 100% | 低 | 极快 | <10万条,精度优先 |
| HNSW | 层级导航小世界图 | 快 | 高(>95%) | 高 | 中 | 10万-千万级,通用首选 |
| IVF_PQ | 倒排+乘积量化 | 快 | 中(85-95%) | 低 | 慢 | 亿级,内存受限场景 |
| SCANN | 各向异性量化 | 最快 | 高 | 中 | 慢 | Milvus专属,大规模 |
HNSW参数调优
-- m: 每个节点的邻居数,越大召回越高但内存越大
-- ef_construction: 构建时的搜索宽度,越大索引质量越高但构建越慢
CREATE INDEX idx_documents_embedding ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- 查询时调整ef_search(pgvector 0.7+)
SET hnsw.ef_search = 100; -- 默认40,增大可提高召回
经验法则:
m=16, ef_construction=64是大多数场景的甜蜜点。ef_search设为预期返回条数的2-5倍。
混合搜索:向量搜索 + BM25关键词搜索
纯粹的向量搜索在精确匹配场景下可能失效(如搜索特定ID、产品编号)。混合搜索将语义搜索与关键词搜索融合,取长补短。
混合搜索架构
用户查询
├──→ Embedding → 向量搜索 → 语义结果(权重α)
├──→ 分词 → BM25搜索 → 关键词结果(权重β)
└──→ RRF融合 → 最终排序
pgvector + 全文检索实现
-- 创建全文检索索引
ALTER TABLE documents ADD COLUMN tsv tsvector
GENERATED ALWAYS AS (to_tsvector('simple', coalesce(title, '') || ' ' || coalesce(content, ''))) STORED;
CREATE INDEX idx_documents_tsv ON documents USING GIN (tsv);
-- 混合搜索:向量相似度 + 全文检索
WITH vector_results AS (
SELECT id, 1 - (embedding <=> '[0.1, 0.2, ...]'::vector) AS vector_score
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 50
),
text_results AS (
SELECT id, ts_rank(tsv, plainto_tsquery('simple', '搜索关键词')) AS text_score
FROM documents
WHERE tsv @@ plainto_tsquery('simple', '搜索关键词')
LIMIT 50
)
SELECT COALESCE(v.id, t.id) AS id,
COALESCE(v.vector_score, 0) * 0.7 + COALESCE(t.text_score, 0) * 0.3 AS hybrid_score
FROM vector_results v
FULL OUTER JOIN text_results t ON v.id = t.id
ORDER BY hybrid_score DESC
LIMIT 20;
RRF(Reciprocal Rank Fusion)
public List<DocumentSearchResult> hybridSearch(String query, int limit) {
float[] embedding = embeddingService.generateEmbedding(query);
List<DocumentSearchResult> vectorResults = vectorRepository.searchSimilar(embedding, 50);
List<DocumentSearchResult> keywordResults = fulltextRepository.search(query, 50);
Map<Long, Double> rrfScores = new HashMap<>();
int k = 60;
for (int i = 0; i < vectorResults.size(); i++) {
Long id = vectorResults.get(i).getId();
rrfScores.merge(id, 1.0 / (k + i + 1), Double::sum);
}
for (int i = 0; i < keywordResults.size(); i++) {
Long id = keywordResults.get(i).getId();
rrfScores.merge(id, 1.0 / (k + i + 1), Double::sum);
}
return rrfScores.entrySet().stream()
.sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
.limit(limit)
.map(entry -> findDocumentById(entry.getKey(), vectorResults, keywordResults))
.collect(Collectors.toList());
}
Rerank重排序
向量搜索的初步结果可能不够精准,Rerank模型对候选集进行精细排序,显著提升精度。
| Rerank模型 | 延迟 | 精度提升 | 开源 | 特点 |
|---|---|---|---|---|
| Cohere Rerank | ~100ms | +15-25% | ❌ | API调用,多语言 |
| bge-reranker-v2-m3 | ~50ms | +10-20% | ✅ | 中文表现优异 |
| Jina Reranker | ~80ms | +10-15% | ✅ | 长文档友好 |
| cross-encoder/ms-marco | ~60ms | +10-15% | ✅ | 经典方案 |
典型流程:向量搜索取Top 50 → Rerank重排 → 返回Top 10。精度提升15-25%,延迟增加50-100ms,通常值得。
生产调优
维度选择
| 维度 | 模型示例 | 存储成本 | 精度 | 适用场景 |
|---|---|---|---|---|
| 384 | MiniLM | 低 | 中 | 对精度要求不高 |
| 768 | BGE-base | 中 | 高 | 通用场景 |
| 1024 | BGE-M3 | 中 | 高 | 多语言 |
| 1536 | OpenAI v3 | 高 | 很高 | 通用高质量 |
| 3072 | OpenAI v3-large | 很高 | 最高 | 精度优先 |
量化压缩
// Matryoshka维度截断:高维向量截断为低维
// 例如1536维 → 768维,存储减半,精度损失<3%
public float[] truncateEmbedding(float[] embedding, int targetDim) {
if (targetDim >= embedding.length) return embedding;
float[] truncated = new float[targetDim];
System.arraycopy(embedding, 0, truncated, 0, targetDim);
return truncated;
}
// 8位量化:float32 → int8,存储减少75%
public byte[] quantizeToInt8(float[] embedding) {
float maxAbs = 0;
for (float f : embedding) maxAbs = Math.max(maxAbs, Math.abs(f));
byte[] quantized = new byte[embedding.length];
for (int i = 0; i < embedding.length; i++) {
quantized[i] = (byte) (embedding[i] / maxAbs * 127);
}
return quantized;
}
分片策略
| 数据规模 | 分片策略 | 说明 |
|---|---|---|
| <100万 | 单节点 | 无需分片 |
| 100万-1000万 | 按业务分表 | 如按分类、按时间 |
| 1000万-1亿 | Hash分片 | 均匀分布到多个分片 |
| >1亿 | Milvus分布式 | 原生支持PB级 |
成本分析:自建 vs 云服务
| 维度 | 自建Milvus | Zilliz Cloud | Pinecone | pgvector自建 |
|---|---|---|---|---|
| 100万条1536维 | ~$50/月 | ~$65/月 | ~$70/月 | ~$30/月 |
| 1000万条1536维 | ~$200/月 | ~$300/月 | ~$350/月 | ~$150/月 |
| 运维成本 | 高 | 无 | 无 | 低 |
| 扩展性 | 需手动 | 自动 | 自动 | 需手动 |
| 数据安全 | 完全可控 | 云端 | 云端 | 完全可控 |
| 学习曲线 | 陡峭 | 低 | 极低 | 低 |
建议:起步阶段用pgvector(已有PG的情况下),日增向量超过10万条时考虑Milvus或Zilliz Cloud。Pinecone适合不想碰基础设施的团队,但成本偏高。
总结
向量数据库和语义搜索不是银弹,但在以下场景中价值巨大:
- 知识库/RAG:让AI基于企业私有数据回答问题
- 推荐系统:基于内容相似度推荐
- 语义去重:识别语义相同但表述不同的内容
- 智能客服:理解用户意图而非关键词匹配
从关键词到语义的范式转变,本质是从"匹配字面"到"理解意图"的升级。 选择合适的向量数据库和Embedding模型,结合混合搜索和Rerank,才能构建真正好用的语义搜索系统。
本站提供浏览器本地工具,免注册即可试用 →