Elasticsearch 8.0 及以上版本新增向量近邻检索 k-nearest neighbor(kNN)search 功能,能够帮助您快速实现图像搜索、视频指纹采样、人脸识别、语音识别和商品推荐等向量检索场景的需求。本文介绍如何使用 kNN search 功能。

背景信息

关于 Elasticsearch 向量近邻检索 k-nearest neighbor(kNN)search 的详细说明,请参见 k-nearest neighbor(kNN)search

前提条件

  • 创建阿里云 Elasticsearch 8.x 版本实例,本文以阿里云 Elasticsearch 8.5.1 版本为例介绍。创建实例的方法,请参见 创建阿里云 Elasticsearch 实例
  • 将业务数据转换成有意义的向量值(根据相似性设计向量,文档的向量与查询向量越接近,向量相似度匹配越好),并将向量数据存储在 dense_vector 类型的字段下。

注意事项

  • 需使用 dense_vector 类型的索引字段存储向量值,且 dense_vector 类型不支持 aggregations sorting。
  • nesetd 字段类型下不支持近似 kNN 查询。
  • Elasticsearch ccs 场景使用 kNN 检索时,不支持 ccs_minimize_roundtrips 参数。
  • kNN 默认使用 dfs_query_then_fetch 查询类型,执行 kNN 查询时,不能显式设置 search_type。

kNN 检索方式说明

kNN支持两种检索方式: 近似kNN 精确kNN ,两者区别如下。
检索方式 查询接口 是否全内存 mapping 要求 特点
近似 kNN 通过 search API 指定 kNN 参数查询 向量字段下 index 参数需要设置为 true,才能开启近似 kNN 查询。
说明 近似 kNN 搜索是在 8.0 版本新增的,在此之前,dense_vector 类型的字段不支持在 mapping 中设置 index true。如果低于 8.0 版本的集群升级到阿里云 Elasticsearch 8.5 版本,并且要使用 kNN 检索,需要确保创建的索引包含 dense_vector 类型的字段。为了支持近似 kNN 搜索,还需要重建索引并且设置新索引 mapping 中的 index true。
近似 kNN 以较慢的索引速度和较低的准确性为代价来降低延迟。
精确 kNN 带向量函数的 script_score 查询。 向量字段下 index 参数设置 false 或不要指定,可提高检索效率。 script_score 查询将扫描每个匹配的文档来计算向量函数,会导致搜索速度变慢。可以通过 query 限制传递给向量函数的文档数改善延迟。

近似 kNN

调整性能

通过近似kNN检索,您可以高效地找到与查询向量最近的K个向量,其搜索方式与其他查询存在差异,因此对集群性能有特殊要求,可参考以下方式调整:
  • Elasticsearch 将每个 segment 的密集向量值以 HNSW 图来存储,因此索引向量数据时主要耗时在 HNSW 图的构建过程中,建议您增加客户端超时时间且使用 bulk 请求写入数据。
  • 降低索引 segment 数或将所有 segment 合并为 1 个来提高检索效率。
  • 数据节点的内存空间大于所有向量数据和索引结构所占空间。
  • 避免在 kNN 检索期间大量写入或更新数据。

创建索引

创建近似kNN时,索引mapping必须设置 index true ,并指定 similarity 参数值。
PUT image-index
  "mappings": {
    "properties": {
      "image-vector": {
        "type": "dense_vector",
        "dims": 3,
        "index": true,
        "similarity": "l2_norm"
      "title": {
        "type": "text"
      "file-type": {
        "type": "keyword"
}
向量参数说明如下,更多参数说明,请参见 dense-vector
参数 说明
type 用来存储浮点数的密集向量。需要设置为 dense_vector。
dims 向量的维度大小。当 index true 时,不能超过 1024;当 index false 时,不能超过 2048 。
index 是否为 kNN 生成新的索引。实现近似 kNN 查询时,需要将 index 设置为 true ,默认为 false。
similarity 文档间的相似度算法。 index true 时,此值必须设置。可选值:
  • l2_norm :计算向量间欧式距离。_score 公式: 1 / (1 + l2_norm(query, vector)^2)
  • dot_product :计算两个向量点积,_score 计算依赖 element_type 参数值。
    • element_type float ,所有向量需归一化为单位长度。_score 公式: (1 + dot_product(query, vector)) / 2
    • element_type byte ,所有向量需要有相同的长度,否则结果不准确。_score 公式: 0.5 + (dot_product(query, vector) / (32768 * dims))
  • cosine :计算向量间的余弦相似度。最有效的 cosine 使用方式是将所有向量归一化为单位长度代替 dot_product。_score 公式: (1 + cosine(query, vector)) / 2
    重要 余弦相似度算法不允许向量数据为 0。

写入数据

POST image-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "image-vector": [1, 5, -20], "title": "moose family", "file-type": "jpg" }
{ "index": { "_id": "2" } }
{ "image-vector": [42, 8, -15], "title": "alpine lake", "file-type": "png" }
{ "index": { "_id": "3" } }
{ "image-vector": [15, 11, 23], "title": "full moon", "file-type": "jpg" }

向量检索

近似向量检索需要 通过search API调用knn参数
说明 knn_search API 在Elasticsearch 8.4版本之后被废弃,请通过在 search API中配置knn参数 的方式进行向量检索。
POST image-index/_search
  "knn": {
    "field": "image-vector",
    "query_vector": [-5, 9, -12],
    "k": 10,
    "num_candidates": 100
  "fields": [ "title", "file-type" ]
}
knn 参数说明如下,详细说明请参见 search-api-knn
参数 是否必选 说明
field 要检索的向量字段名称。
query_vector 查询向量,必须与 field 指定的向量数据具有相同的维度。
k 返回的最近邻对象的数量。 k 的值需要小于 num_candidates
num_candidates 每个分片上需查找的最近邻候选对象的个数,不能超过 10000。
说明 增加 num_candidates 的值可提高最终 K 值的准确性,但相应搜索速度会变慢。
filter 通过 DSL 语句过滤文档。kNN 从过滤后的文档中返回前 K 个文档,如果不指定过滤器,将对所有文档做 kNN 近似计算。

精确 kNN

创建索引

PUT zl-index
  "mappings": {
    "properties": {
      "product-vector": {
        "type": "dense_vector",
        "dims": 5,
        "index": false
      "price": {
        "type": "long"
}
定义向量字段部分参数说明如下,更多参数说明请参见 dense-vector
参数 说明
type 用来存储浮点数的密集向量,需要设置为 dense_vector
dim 向量的维度大小。
index 是否为 kNN 生成新的索引文件。默认值为 false 。使用精确 kNN 检索,可不配置 index 参数或将其设置为 false ,可提高精确 kNN 的检索效率。

写入数据

POST zl-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "product-vector": [230.0, 300.33, -34.8988, 15.555, -200.0], "price": 1599 }
{ "index": { "_id": "2" } }
{ "product-vector": [-0.5, 100.0, -13.0, 14.8, -156.0], "price": 799 }
{ "index": { "_id": "3" } }
{ "product-vector": [0.5, 111.3, -13.0, 14.8, -156.0], "price": 1099 }

查询向量

以下示例在 script_score 查询中指定向量函数 cosineSimilarity ,并使用 script_score.query 指定过滤器限制传递给vector文档数来降低搜索延迟。
POST zl-index/_search
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "range" : {
              "price" : {
                "gte": 1000
      "script": {
        "source": "cosineSimilarity(params.queryVector, 'product-vector') + 1.0",
        "params": {
          "queryVector": [-0.5, 90.0, -10, 14.8, -156.0]
}
script_score 支持以下向量函数,更多说明请参见 向量访问
函数名 说明
cosineSimilarity 计算查询向量和文档向量的余弦相似度。
dotProduct 计算查询向量和文档向量之间的点乘距离。
l1norm 计算查询向量和文档向量之间的 L1 距离(曼哈顿距离)。
l2norm 计算查询向量和文档向量之间的 L2 距离(欧式距离)。