Lindorm向量引擎支持向量数据检索功能,兼容Elasticsearch协议,同时支持标量、向量、全文混合检索功能。本文介绍如何通过curl命令连接并使用向量引擎。
前提条件
算法说明
您可以参考各个算法的特点、适用的数据量等,创建适合业务场景的向量索引。
算法 | 数据量 | 资源占用 | 算法特点 | 注意事项 |
flat | [0,1万) | 纯内存。 | 暴力检索,使用简单,适合小数据集。 | 检索性能会随着数据量增加而降低。 |
hnsw | [1万, 100万) | 纯内存,资源占用较高。 | 在线索引,适合数据集中等规模,用法相对简单。 | CPU占用量与number_of_shards(索引分片数量)的值相关,其值通常设置为向量引擎节点数。如果索引数量较多,且每个索引数据量较少,将number_of_shards设置为 |
ivfpq | 100万以上 | 磁盘索引,默认1:8压缩,即内存大小为原始数据的1/8。 | 离线索引,需要先写入一定量的数据再构建索引,适合大数据集,成本较低。 | 发起索引构建时需确保写入的数据量充足,必须大于256条且超过nlist的30倍。 建议在离线数据导入完成后再触发索引构建。索引构建完成后,您可以正常进行KNN查询和写入操作。 说明 nlist参数的说明,请参见参数说明。 |
创建向量索引
创建向量索引,其中vector1
为向量列、field1
为普通列。向量列及其相关参数必须在创建索引时通过mappings结构显式指定。
hnsw类型索引
curl -u <username>:<password> -H 'Content-Type: application/json' -XPUT "http://<URL>/vector_test?pretty" -d '
{
"settings" : {
"index": {
"number_of_shards": 2,
"knn": true
}
},
"mappings": {
"_source": {
"excludes": ["vector1"]
},
"properties": {
"vector1": {
"type": "knn_vector",
"dimension": 3,
"data_type": "float",
"method": {
"engine": "lvector",
"name": "hnsw",
"space_type": "l2",
"parameters": {
"m": 24,
"ef_construction": 200
}
}
},
"field1": {
"type": "long"
}
}
}
}'
ivfpq类型索引
curl -u <username>:<password> -H 'Content-Type: application/json' -XPUT "http://<URL>/vector_ivfpq_test?pretty" -d '
{
"settings": {
"index": {
"number_of_shards": 4,
"knn": true,
"knn.offline.construction": true
}
},
"mappings": {
"_source": {
"excludes": ["vector1"]
},
"properties": {
"vector1": {
"type": "knn_vector",
"dimension": 3,
"data_type": "float",
"method": {
"engine": "lvector",
"name": "ivfpq",
"space_type": "cosinesimil",
"parameters": {
"m": 3, // 务必与维度dimension保持一致
"nlist": 10000,
"centroids_use_hnsw": true,
"centroids_hnsw_m": 48,
"centroids_hnsw_ef_construct": 500,
"centroids_hnsw_ef_search": 200
}
}
},
"field1": {
"type": "long"
}
}
}
}
'
创建ivfpq类型索引,您必须注意以下事项:
ivfpq中knn.offline.construction务必设置为
true
,后续需要写入一定量的数据才能发起构建索引。使用ivfpq算法请将dimension替换业务真实向量维度,并将m值设置为与dimension相同的值。
稀疏向量索引
curl -u <username>:<password> -H 'Content-Type: application/json' -XPUT "http://<URL>/vector_sparse_test?pretty" -d '
{
"settings" : {
"index": {
"number_of_shards": 2,
"knn": true
}
},
"mappings": {
"_source": {
"excludes": ["vector1"]
},
"properties": {
"vector1": {
"type": "knn_vector",
"data_type": "sparse_vector",
"method": {
"engine": "lvector",
"name": "sparse_hnsw",
"space_type": "innerproduct",
"parameters": {
"m": 24,
"ef_construction": 200
}
}
},
"field1": {
"type": "long"
}
}
}
}'
参数说明
连接参数说明
参数 | 是否必填 | 示例值 | 说明 |
URL | 是 | ld-bp106782jm96****-proxy-search-vpc.lindorm.aliyuncs.com:30070 | 搜索引擎的Elasticsearch兼容连接地址。如何获取,请参见Elasticsearch兼容地址。 |
username | 是 | xltest | 访问向量引擎的用户名和密码。 默认用户名和密码的获取方式:在控制台的左侧导航栏,选择数据库连接,单击搜索引擎页签,在搜索引擎页签可获取。 |
password | 是 | test |
向量列参数说明
通用参数
参数 | 是否必填 | 说明 |
type | 是 | 索引列的类型。对于向量列,固定为 |
dimension | 是 | 向量数据的维度。取值范围:[1,32768]。 |
data_type | 否 | 向量数据的类型。目前支持以下类型:
|
method.name | 是 | 向量数据的索引算法。取值如下:
|
method.space_type | 否 | 向量数据的距离算法。取值如下:
|
HNSW算法参数
参数 | 是否必填 | 说明 |
method.parameters.m | 否 | 每一层图的最大出边数量。 取值范围:[1,100]。默认值为 |
method.parameters.ef_construction | 否 | 索引构建时动态列表的长度。 取值范围:[1,1000]。默认值为 |
IVFPQ算法参数
参数 | 是否必填 | 说明 |
method.parameters.m | 否 | 量化中子空间的数量。取值范围:[2,32768]。默认值为 重要 创建ivfpq类型索引时,该参数值必须与通用参数dimension的值相同。 |
method.parameters.nlist | 否 | 聚类中心的数量。 取值范围:[2, 1000000]。默认值为 |
method.parameters.centroids_use_hnsw | 否 | 是否在聚类中心搜索时使用HNSW算法。 取值如下:
|
method.parameters.centroids_hnsw_m | 否 | 若在聚类中心搜索时使用HNSW算法,设定HNSW算法的每一层图的最大出边数量。 取值范围:[1,100]。默认值为 |
method.parameters.centroids_hnsw_ef_construct | 否 | 若在聚类中心搜索时使用HNSW算法,设定HNSW算法在索引构建时动态列表的长度。 取值范围:[1,1000]。默认值为 |
method.parameters.centroids_hnsw_ef_search | 否 | 若在聚类中心搜索时使用HNSW算法,设定HNSW算法在查询时动态列表的长度。 取值范围:[1,1000]。默认值为 |
假设向量索引名为vector_test
,返回结果如下:
{"acknowledged":true,"shards_acknowledged":true,"index":"vector_test"}
数据写入
包含向量列的索引与普通索引的数据写入方式一致,以向量数组(Float数组)格式写入,例如[1.2, 1.3, 1.4]
。
单条写入
curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/<索引名称>/_doc/1?pretty" -d '
{"field1": 1, "vector1": [1.2, 1.3, 1.4]}'
以索引vector_test
为例,返回结果如下:
{"_index":"vector_test","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":1,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}
批量写入
curl -u <username>:<password> -H 'Content-Type: application/json' -XPUT "http://<URL>/_bulk?pretty" -d '
{ "index" : { "_index" : "vector_test", "_id" : "2" } }
{ "field1" : 1, "vector1": [2.2, 2.3, 2.4]}
{ "index" : { "_index" : "vector_test", "_id" : "3" } }
{ "field1" : 2, "vector1": [1.2, 1.3, 4.4]}
{ "delete" : { "_index" : "vector_test", "_id" : "2" } }
{ "update" : {"_id" : "1", "_index" : "vector_test"} }
{ "doc" : {"field1" : 3, "vector1": [2.2, 3.3, 4.4]} }
'
插入写
若目标数据已存在,则不允许写入。
curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/_bulk?pretty" -d '
{ "create" : { "_index" : "vector_test", "_id" : "1" } }
{ "field1" : 1, "vector1": [2.2, 2.3, 2.4]}
{ "create" : { "_index" : "vector_test", "_id" : "2" } }
{ "field1" : 2, "vector1": [1.2, 1.3, 4.4]}
'
更新写
若目标数据已存在,则执行更新操作;若目标数据不存在,写入数据。
curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/_bulk?pretty" -d '
{ "index" : { "_index" : "vector_test", "_id" : "3" } }
{ "field1" : 1, "vector1": [2.2, 2.3, 2.4]}
{ "index" : { "_index" : "vector_test", "_id" : "4" } }
{ "field1" : 2, "vector1": [1.2, 1.3, 4.4]}
'
稀疏向量写入
写入方式与上述方式相同,但需要修改vector1的格式。共支持两种格式:JSON STRING和JSON Object。前者性能更优,后者格式更友好。
JSON STRING
curl -u <username>:<password> -H 'Content-Type: application/json' -XPUT "http://<URL>/vector_sparse_test/_doc/1?pretty" -d '{
"field1": 2,
"vector1": "{\"indices\": [10, 14, 16], \"values\": [0.5, 0.5, 0.2]}"
}'
JSON Object
curl -u <username>:<password> -H 'Content-Type: application/json' -XPUT "http://<URL>/vector_sparse_test/_doc/1?pretty" -d '{
"field1": 1,
"vector1": {"indices": [10, 12, 16], "values": [0.5, 0.5, 0.2]}
}'
索引构建
除ivfpq索引,其他类型索引创建时index.knn.offline.construction默认为
false
,即在线索引,无需手动构建。在触发ivfpq索引构建前需注意:在创建ivfpq索引时,需将index.knn.offline.construction显式指定为
true
,且在发起构建时务必确保已写入足够的数据量,必须大于256条且超过nlist的30倍。手动触发索引构建完成后,后续可正常写入和查询,无需再次构建索引。
触发构建
curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/_plugins/_vector/index/build" -d '
{
"indexName": "vector_ivfpq_test",
"fieldName": "vector1",
"removeOldIndex": "true"
}'
参数说明
参数 | 是否必填 | 说明 |
indexName | 是 | 表名称,例如 |
fieldName | 是 | 针对哪个字段构建索引,例如 |
removeOldIndex | 是 | 构建索引时,是否删除旧的索引。取值如下:
|
返回结果如下:
{
"payload": ["default_vector_ivfpq_test_vector1"]
}
返回结果为索引构建生成的taskId
。
查看索引状态
您可以通过以下方式查看索引构建状态。
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/_plugins/_vector/index/tasks" -d '{
"indexName": "vector_ivfpq_test",
"fieldName": "vector1",
"taskIds": "[\"default_vector_ivfpq_test_vector1\"]"
}'
其中,taskIds为触发构建时生成的taskId
,可以填写空的数组,例如"taskIds": "[]"
,效果与上述已填写taskIds的效果一致。
返回结果如下:
{
"payload": ["task: default_vector_ivfpq_test_vector1, stage: FINISH, innerTasks: xxx, info: finish building"]
}
其中,stage表示构建状态,共包含以下几种状态:START(开始构建)、TRAIN(训练阶段)、BUILDING(构建中)、ABORT(终止构建)、FINISH(构建完成)和FAIL(构建失败)。
ABORT通常调用/index/abort接口来终止索引构建。
终止构建
终止索引的构建流程。状态为FINISH
的索引不支持调用该方法。
curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/_plugins/_vector/index/tasks/abort" -d '{
"indexName": "vector_ivfpq_test",
"fieldName": "vector1",
"taskIds": "[\"default_vector_ivfpq_test_vector1\"]"
}'
数据查询
纯向量数据查询
纯向量数据的查询可以通过knn结构实现。
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/<索引名称>/_search?pretty" -d '
{
"size": 10,
"query": {
"knn": {
"vector1": {
"vector": [2.3, 3.3, 4.4],
"k": 10
}
}
},
"ext": {"lvector": {"min_score": "0.01"}}
}'
参数说明
参数结构 | 参数 | 是否必填 | 说明 |
knn | vector | 是 | 查询时使用的向量。 |
k | 是 | 返回最相似的K个数据。 重要 在纯向量检索场景中,建议将size和k设置为相同的值。 | |
ext | lvector.min_score | 否 | 相似度阈值,要求返回的向量得分大于该值。返回的向量得分范围为[0,1]。 取值范围:[0,+inf]。默认值为 |
lvector.filter_type | 否 | 融合查询使用的模式。取值如下:
默认值为空。 | |
lvector.ef_search | 否 | HNSW算法中,索引构建时动态列表的长度。只能用于HNSW算法。 取值范围:[1,1000]。默认值为 | |
lvector.nprobe | 否 | 要查询的聚类单元(cluster units)的数量。请根据您的召回率要求,对该参数的值进行调整已达到理想效果。值越大,召回率越高,搜索性能越低。 取值范围:[1,method.parameters.nlist]。无默认值。 重要 仅适用于ivfpq算法。 | |
lvector.reorder_factor | 否 | 使用原始向量创建重排序(reorder)。ivfpq算法计算的距离为量化后的距离,会有一定的精度损失,需要使用原始向量进行重排序。比例为 取值范围:[1,200]。默认值为 重要
|
以hnsw索引vector_test
为例,返回结果如下:
返回指定字段
如果需要在查询时返回指定字段,可以指定 "_source": ["field1", "field2"]
或使用"_source": true
返回非向量的全部字段。以查询索引vector_test
为例,使用方法如下:
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/vector_test/_search?pretty" -d '
{
"size": 10,
"_source": ["field1"],
"query": {
"knn": {
"vector1": {
"vector": [2.2, 2.3, 2.4],
"k": 10
}
}
},
"ext": {"lvector": {"min_score": "0.01"}}
}'
返回结果如下:
hsnw算法查询
以查询hnsw索引vector_test
为例,使用方法如下:
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/vector_test/_search?pretty" -d '
{
"size": 10,
"query": {
"knn": {
"vector1": {
"vector": [2.2, 2.3, 2.4],
"k": 10
}
}
},
"ext": {"lvector": {"ef_search": "100"}}
}'
ivfpq算法查询
以查询ivfpq索引vector_ivfpq_test
为例,使用方法如下:
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/vector_ivfpq_test/_search?pretty" -d '
{
"size": 10,
"query": {
"knn": {
"vector1": {
"vector": [2.2, 2.3, 2.4],
"k": 10
}
}
},
"ext": {"lvector": {"nprobe": "60", "reorder_factor": "5"}}
}'
如果k值相对较大,如大于100,将reorder_factor的值设置为
1
即可。当nlist的值为
10000
时,可以先将nprobe设置为60
,查看检索效果。如果想继续提升召回率,可适当增加nprobe的值,如80、100、120、140、160,该值引起的性能损耗远小于reorder_factor,但也不适宜设置过大。
稀疏向量查询
查询方式与上述方式相同,但需要修改vector1的格式。共支持两种格式:JSON STRING和JSON Object。前者性能更优,后者格式更友好。
JSON STRING
以查询稀疏向量索引vector_sparse_test
为例,使用方法如下:
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/vector_sparse_test/_search?pretty" -d '
{
"size": 10,
"query": {
"knn": {
"vector1": {
"vector": "{\"indices\": [10, 45, 16], \"values\": [0.5, 0.5, 0.2]}",
"k": 10
}
}
}
}'
JSON Object
以查询稀疏向量索引vector_sparse_test
为例,使用方法如下:
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/vector_sparse_test/_search?pretty" -d '
{
"size": 10,
"query": {
"knn": {
"vector1": {
"vector": {"indices": [10, 45, 16], "values": [0.5, 0.5, 0.2]},
"k": 10
}
}
}
}'
融合查询
向量列的查询可与普通列的查询条件结合,并返回综合的查询结果。在实际业务使用时, Post_Filter近似查询通常能获取更相似的检索结果。
Pre-Filter近似查询
通过在knn查询结构内部添加过滤器filter,并指定filter_type参数的值为pre_filter
,可实现先过滤结构化数据,再查询向量数据。
目前结构化过滤数据的上限为10,000条。
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/<索引名称>/_search?pretty" -d '
{
"size": 10,
"query": {
"knn": {
"vector1": {
"vector": [2.3, 3.3, 4.4],
"filter": {
"range": {
"field1": {
"gte": 0
}
}
},
"k": 10
}
}
},
"ext": {"lvector": {"filter_type": "pre_filter"}}
}'
Post-Filter近似查询
通过在knn查询结构内部添加过滤器filter,并指定filter_type参数的值为post_filter
,可实现先查询向量数据,再过滤结构化数据。
在使用Post_Filter近似查询时,可以适当将k的值设置大一些,以便获取更多的向量数据再进行过滤。
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/<索引名称>/_search?pretty" -d '
{
"size": 10,
"query": {
"knn": {
"vector1": {
"vector": [2.3, 3.3, 4.4],
"filter": {
"range": {
"field1": {
"gte": 0
}
}
},
"k": 10
}
}
},
"ext": {"lvector": {"filter_type": "post_filter"}}
}'
在使用Post_Filter近似查询时需要适当放大k的值,如果使用ivfpq算法,还需要调整reorder_factor的值。具体使用如下:
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/<ivfpq索引名称>/_search?pretty" -d '
{
"size": 10,
"query": {
"knn": {
"vector1": {
"vector": [2.2, 2.3, 2.4],
"filter": {
"range": {
"field1": {
"gte": 0
}
}
},
"k": 1000
}
}
},
"ext": {"lvector": {"filter_type": "post_filter","nprobe": "60", "reorder_factor": "1"}}
}'
在Post_Filter近似查询场景中,可以将k值放大至10,000、最大控制在20,000之内,从而将处理时延控制在百毫秒之内。如果k值相对较大,将reorder_factor的值设置为
1
即可。当nlist的值为
10000
时,可以先将nprobe设置为60,查看检索效果。如果检索效果不理想,可适当增加nprobe的值,如80、100、120、140、160,该值引起的性能损耗远小于reorder_factor,但也不宜设置过大。
您也可以通过post_filter添加过滤条件,实现Post-Filter近似查询。
curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/<索引名称>/_search?pretty" -d '
{
"size": 10,
"query": {
"knn": {
"vector1": {
"vector": [2.3, 3.3, 4.4],
"k": 10
}
}
},
"post_filter": {
"range": {
"field1": {
"gte": 0
}
}
}
}'
常规用法
以下以hnsw索引vector_test
为例,介绍索引基础的查询、删除等常规使用方法。
查询所有索引及其数据量。
curl -u <username>:<password> -XGET "http://<URL>/_cat/indices?v"
返回结果:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size green open vector_test vector_test 2 0 2 0 6.8kb 6.8kb
查询指定索引的数据量。以查询
vector_test
为例。curl -u <username>:<password> -XGET "http://<URL>/vector_test/_count?pretty"
返回结果:
{ "count" : 2, "_shards" : { "total" : 2, "successful" : 2, "skipped" : 0, "failed" : 0 } }
查看索引创建信息。
curl -u <username>:<password> -XGET "http://<URL>/vector_test?pretty"
返回结果:
删除整个索引。
curl -u <username>:<password> -XDELETE "http://<URL>/vector_test"
通过查询删除。
curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/vector_test/_delete_by_query" -d ' { "query": { "term": { "field1": 1 } } }'