Administrator
发布于 2023-11-10 / 11775 阅读
53

Elasticsearch 8 向量检索能力实践

想给搜索加上"语义"

我们的商品搜索一直靠关键词匹配,用户搜"适合送妈妈的礼物"这种长尾 query 基本搜不到,因为标题里没有这几个字。运营天天抱怨转化差。ES 8 增强了 dense_vector 和 kNN 检索,我试着把商品标题做成向量塞进去,让语义相近的也能召回,点击率明显好转。这里把建模、查询和选型一并记下来。

dense_vector 字段建模

先建一个带向量字段的索引,维度跟模型走(我们用 768 维的 bge 中文模型,本地用 ONNX 推理):

PUT /products
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },
      "title_vec": {
        "type": "dense_vector",
        "dims": 768,
        "index": true,
        "similarity": "cosine"
      }
    }
  }
}

写入时把 title 用模型推理成向量塞进 title_vec。注意 ES 8 的向量默认走 HNSW 近似索引,构建时会多占内存,segment 合并时也有开销。我们 200 万商品,向量字段约占 6GB 堆外,建索引时把 refresh_interval 调大减少合并压力。

kNN 查询

查询时把用户 query 也向量化,用 knn 子句召回最近的 N 个:

GET /products/_search
{
  "knn": {
    "field": "title_vec",
    "query_vector": [0.12, -0.03, ...],  // 768 维
    "k": 10,
    "num_candidates": 100
  },
  "query": { "match": { "title": "礼物" } }
}

我们还把 kNN 和传统的 BM25 关键词查询用 bool 组合,语义召回和精确匹配互补。实测"送妈妈的礼物"这种 query 的点击率从 11% 升到 34%,长尾 query 的覆盖明显变广。

和专门向量库的差距

也别神话它。我对照测了 Milvus 和纯 ES,在 200 万向量规模下:

维度ES 8 向量Milvus
查询延迟(P99)23ms3ms
写入吞吐受限于分片更高
运维复杂度复用现有 ES 集群单独一套
混合检索原生支持需自研

结论很清楚:数据量在千万级以内、又要和现有文本检索混合,ES 8 向量是性价比之选;真要做亿级纯向量召回,还是得上 Milvus 或专用向量数据库。我们当前量级和混合检索需求,ES 最省事。

小结

ES 8 的 dense_vector 加 kNN 让"搜索加语义"能在一个系统里完成,省了引入新组件的运维负担。代价是向量检索性能和扩展性不如专业库。选型时先问自己数据规模和要不要混合检索,别盲目追新也别一味守旧。我们已经把语义召回作为主搜的补充通道跑了一个月,稳定。

参考