
3.5 Elasticsearch-向量搜索:dense_vector + cosineSimilarity(8.0+)
3.5.1 为什么需要向量搜索
文本、图像、音频一旦被 Embedding 模型映射成固定维度的稠密向量,「语义相似」就等价于「向量空间中距离相近」。Elasticsearch 从 7.x 开始提供 dense_vector 字段,8.0 之后正式引入基于 HNSW 的近似最近邻(ANN)检索,把原来只能暴力循环的 script_score 模式推进到毫秒级响应,使 ES 从「全文检索引擎」升级为「混合语义检索引擎」。
3.5.2 字段设计:index or not index
dense_vector 有两种使用方式,选型直接影响吞吐与精度:
-
不建索引(index: false)
仅当存储,搜索阶段用script_score暴力计算cosineSimilarity,实现精确但 O(N) 的「暴力 kNN」。适合万级以下、低频查询或实验室场景。PUT product { "mappings": { "properties": { "title": { "type": "text" }, "title_vector": { "type": "dense_vector", "dims": 768, "index": false } } } } -
建 HNSW 索引(index: true,8.0+ 默认)
在写入时构建多层导航图,查询走近似 kNN,复杂度降至 O(logN)。必须同时指定similarity,否则无法建索引。PUT product { "mappings": { "properties": { "title_vector": { "type": "dense_vector", "dims": 768, "index": true, "similarity": "cosine" } } } }注意:
- 维度≤1024(float 型)
- 相似度可选
l2_norm、dot_product、cosine;cosine会在入库时自动将向量归一化,查询侧无需再处理 - 若用
bit元素类型做超高维场景,维度必须是 8 的倍数,相似度固定为hamming
3.5.3 写入数据:让 ES 帮你归一化
当 similarity: cosine 时,ES 会在索引阶段自动计算 L2 范数并把向量归一化,因此客户端可直接送入原始浮点数组,省去额外的归一化步骤。
POST product/_bulk
{"index":{}}
{"title":"轻便夏季背包","title_vector":[0.12,-0.33,...,0.45]}
3.5.4 近似 kNN 查询:knn 段
POST product/_search
{
"knn": {
"field": "title_vector",
"query_vector": [0.11,-0.30,...,0.44],
"k": 10,
"num_candidates": 100
},
"_source": ["title", "_score"]
}
k:最终返回的 Top-Knum_candidates:在 HNSW 图中选取的候选集大小,越大越准也越慢,通常取k*10~20做权衡- 结果
_score已归一化到[0,1],值越大越相似
3.5.5 混合评分:向量 + 布尔过滤
向量查询往往伴随业务过滤(类目、库存、上下架)。ES 8 允许在 knn 同级写 filter,先过滤再 ANN,大幅减少候选集:
POST product/_search
{
"knn": {
"field": "title_vector",
"query_vector": [...],
"k": 10,
"num_candidates": 100,
"filter": { "term": { "on_sale": true } }
}
}
3.5.6 精确 kNN:script_score 保底
对超小表或需要 100% 精确的场景,仍可用 script_score 暴力计算。注意把 cosineSimilarity 结果 +1.0 消除负分,以便与其他评分函数统一尺度:
POST product/_search
{
"query": {
"script_score": {
"query": { "match_all": {} },
"script": {
"source": "cosineSimilarity(params.q, 'title_vector') + 1.0",
"params": { "q": [...] }
}
}
}
}
经验:
- 若数据>5 万条,延迟会肉眼可见,建议切到 HNSW
- 向量维度>300 时,开启
index:true的召回率下降通常<2%,却带来 10~50 倍提速
3.5.7 性能调优清单
- 维度控制:embedding 模型输出 768/1024 即可,再高手动 PCA 降维
- 段合并:ANN 性能与段数负相关,写入后定时
POST /product/_forcemerge?max_num_segments=1 - 缓存:kNN 结果默认不走 Query Cache,若查询向量重复率高,可在客户端做向量→结果映射缓存
- 并行分片:
routing=user_id把同一用户数据写到一个分片,减少广播 - 硬件:HNSW 对内存带宽敏感,建议 1 GB 堆外内存/50 万 768 维向量做预算,使用大页内存
vm.max_map_count=262144
3.5.8 常见坑
- 维度不一致直接拒绝写入;先检查模型升级后是否改了输出维数
cosineSimilarity入参向量未归一化会导致dotProduct与cosine混用结果偏差——让 ES 做归一化即可- 使用
script_score时忘记+1.0,结果出现负分,与后续function_score累加时产生倒序 - 8.0 之前老集群误用
knn查询会报no such query,需升级或继续用script_score
3.5.9 小结
借助 dense_vector + cosineSimilarity,Elasticsearch 8.0+ 在原生倒排索引之上叠加了 HNSW 向量索引,实现「毫秒级近似语义检索」。上线流程可简化为:
- 建模→
index:true+similarity:cosine - 写入→ES 自动归一化
- 查询→
knn段带业务过滤 - 调优→维度、段合并、内存
当数据量或 QPS 继续膨胀,可再叠加向量压缩、分片裁剪、缓存等手段,把 ES 打造成兼顾全文、结构化、向量三位一体的统一检索引擎。
更多技术文章见公众号: 大城市小农民
Elasticsearch 8.0+向量搜索实战
1194

被折叠的 条评论
为什么被折叠?



