Vearch 系统介绍¶
概述¶
Vearch 是对大规模深度学习向量进行高性能相似搜索的弹性分布式系统。
整体架构¶

数据模型: 空间,文档,向量,标量。
组件: Master,Router,PartitionServer。
Master: 负责schema管理,集群级别的源数据和资源协调。
Router: 提供RESTful API: create、delete、search、update; 请求路由转发及结果合并。
PartitionServer(PS): 基于raft复制的文档分片; Gamma向量搜索引擎,它提供了存储、索引和检索向量、标量的能力。
功能简介¶
1、支持CPU与GPU两种版本。
2、支持实时添加数据到索引。
3、支持单个文档定义多个向量字段, 添加、搜索批量操作。
4、支持数值字段范围过滤与string字段标签过滤。
5、支持IVFPQ、HNSW、二进制等索引方式(HNSW、二进制方式4月下旬发布)。
6、支持Python SDK本地快速开发验证。
7、支持机器学习算法插件方便系统部署使用。
系统特性¶
1、自研gamma引擎,提供高性能的向量检索。
2、IVFPQ倒排索引支持compaction,检索性能不受文档更新次数的影响。
3、支持内存、磁盘两种数据存储方式,支持超大数据规模。
4、基于raft协议实现数据多副本存储。
5、支持内积(InnerProduct)与欧式距离(L2)方法计算向量距离。
安装使用¶
编译¶
环境依赖
- 支持CentOS, Ubuntu and Mac OS。推荐CentOS 7.2以上, 检查安装cmake、make工具。
- Go版本>=1.11.2。
- Gcc版本>=5。
- Faiss master分支(commit:833d417db1b6b6fd4b19e092f735373f07eab33f)。
- 如果使用RocksDB, 版本6.2.2, 使用RocksDB中INSTALL.md文件的make shared_lib进行编译。
- 如果使用GPU, CUDA版本>=9.0
编译
进入GOPATH工作目录,
cd $GOPATH/src
mkdir -p github.com/vearch
cd github.com/vearch
下载源代码:
git clone https://github.com/vearch/vearch.git
(后续使用$vearch 代表vearch目录绝对路径)下载gamma代码:
cd vearch
git submodule update --init --recursive
如果使用GPU版本, 修改$vearch/engine/CMakeLists.txt文件中BUILD_WITH_GPU值为on
编译gamma
cd $vearch/engine
mkdir build && cd build
export FAISS_HOME=faiss安装路径
- 如果使用RocksDB
export ROCKSDB_HOME=RocksDB安装路径
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$vearch/ps/engine/gammacb/lib ..
make -j && make install
编译vearch
cd $vearch
export FAISS_HOME=faiss安装路径
- 如果使用RocksDB
export ROCKSDB_HOME=RocksDB安装路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$FAISS_HOME/lib:$ROCKSDB_HOME:$ROCKSDB_HOME/lib:$vearch/ps/engine/gammacb/lib/lib
go build -a --tags=vector -o vearch
生成
vearch
文件表示编译成功
部署¶
单机模式:
- 生成配置文件config.toml(master_server端口使用8817, router_server端口使用9001)
[global]
# the name will validate join cluster by same name
name = "vearch"
# you data save to disk path ,If you are in a production environment, You'd better set absolute paths
data = ["datas/"]
# log path , If you are in a production environment, You'd better set absolute paths
log = "logs/"
# default log type for any model
level = "info"
# master <-> ps <-> router will use this key to send or receive data
signkey = "vearch"
skip_auth = true
# if you are master, you'd better set all config for router、ps and router, ps use default config it so cool
[[masters]]
# name machine name for cluster
name = "m1"
# ip or domain
address = "127.0.0.1"
# api port for http server
api_port = 8817
# port for etcd server
etcd_port = 2378
# listen_peer_urls List of comma separated URLs to listen on for peer traffic.
# advertise_peer_urls List of this member's peer URLs to advertise to the rest of the cluster. The URLs needed to be a comma-separated list.
etcd_peer_port = 2390
# List of this member's client URLs to advertise to the public.
# The URLs needed to be a comma-separated list.
# advertise_client_urls AND listen_client_urls
etcd_client_port = 2370
[router]
# port for server
port = 9001
[ps]
# port for server
rpc_port = 8081
# raft config begin
raft_heartbeat_port = 8898
raft_replicate_port = 8899
heartbeat-interval = 200 #ms
raft_retain_logs = 10000
raft_replica_concurrency = 1
raft_snap_concurrency = 1
- 启动
启动vearch前,需要设置LD_LIBRARY_PATH环境变量的值,添加faiss, gamma, rocksdb等依赖的lib包(比如$vearch/ps/engine/gammacb/lib/lib、$FAISS_HOME/lib等目录下的lib包; 执行ldd vearch 和 ldd $vearch/ps/engine/gammacb/lib/lib/libgamma.so.0.1 命令可以查看vearch和gamma依赖的包)。
./vearch -conf config.toml
集群模式:
- vearch 有三个模块:
ps
,master
,router
, run./vearch -conf config.toml ps/router/master
启动相应模块
假如有5台机器, 2台作为master管理, 2台作为ps计算节点, 1台请求转发
- master
- 192.168.1.1
- 192.168.1.2
- ps
- 192.168.1.3
- 192.168.1.4
- router
- 192.168.1.5
- 生成toml格式配置文件 config.toml, 作为master的机器ip配置在[[masters]]中,支持多个,router和ps所在机器ip无需配置。
[global]
name = "vearch"
data = ["datas/"]
log = "logs/"
level = "info"
signkey = "vearch"
skip_auth = true
# if you are master, you'd better set all config for router、ps and router, ps use default config it so cool
[[masters]]
name = "m1"
address = "192.168.1.1"
api_port = 8817
etcd_port = 2378
etcd_peer_port = 2390
etcd_client_port = 2370
[[masters]]
name = "m2"
address = "192.168.1.2"
api_port = 8817
etcd_port = 2378
etcd_peer_port = 2390
etcd_client_port = 2370
[router]
port = 9001
skip_auth = true
[ps]
rpc_port = 8081
raft_heartbeat_port = 8898
raft_replicate_port = 8899
heartbeat-interval = 200 #ms
raft_retain_logs = 10000
raft_replica_concurrency = 1
raft_snap_concurrency = 1
- 启动vearch前,设置LD_LIBRARY_PATH环境变量加载依赖包
- on 192.168.1.1 , 192.168.1.2 run master
./vearch -conf config.toml master
- on 192.168.1.3 , 192.168.1.4 run ps
./vearch -conf config.toml ps
- on 192.168.1.5 run router
./vearch -conf config.toml router
集群监控¶
http://master_server代表master服务
集群状态¶
curl -XGET http://master_server/_cluster/stats
健康状态¶
curl -XGET http://master_server/_cluster/health
端口状态¶
curl -XGET http://master_server/list/server
副本扩容缩容¶
curl -XPOST -H "content-type: application/json" -d'
{
"partition_id":1,
"node_id": 1,
"method": 0
}
' http://master_server/partition/change_member
method=0: node id 1上添加分片id 1 的副本; method=1: 删除 node id 1 上 分片 id 1 的副本。
库操作¶
http://master_server代表master服务,$db_name是创建的库名
查看集群中所有库¶
curl -XGET http://master_server/list/db
创建库¶
curl -XPUT -H "content-type:application/json" -d '{
"name": "db_name"
}
' http://master_server/db/_create
查看库¶
curl -XGET http://master_server/db/$db_name
查看指定库下所有表空间¶
curl -XGET http://master_server/list/space?db=$db_name
表空间操作¶
http://master_server代表master服务,$db_name是创建的库名, $space_name是创建的表空间名
创建表空间¶
curl -XPUT -H "content-type: application/json" -d'
{
"name": "space1",
"partition_num": 1,
"replica_num": 1,
"engine": {
"name": "gamma",
"index_size": 70000,
"id_type": "String",
"retrieval_type": "IVFPQ",
"retrieval_param": {
"ncentroids": 256,
"nsubvector": 32
}
},
"properties": {
"field1": {
"type": "keyword"
},
"field2": {
"type": "integer"
},
"field3": {
"type": "float",
"index": true
},
"field4": {
"type": "string",
"array": true,
"index": true
},
"field5": {
"type": "integer",
"index": true
},
"field6": {
"type": "vector",
"dimension": 128
},
"field7": {
"type": "vector",
"dimension": 256,
"format": "normalization",
"store_type": "RocksDB",
"store_param": {
"store_param": 2048,
"compress": false
}
}
}
}
' http://master_server/space/$db_name/_create
参数说明:
字段标识 | 字段含义 | 类型 | 是否必填 | 备注 |
---|---|---|---|---|
name | 空间名称 | string | 是 | |
partition_num | 分片数量 | int | 是 | |
replica_num | 副本数量 | int | 是 | |
engine | 引擎配置 | json | 是 | 引擎配置 |
properties | 空间配置 | json | 是 | 定义表字段及类型 |
1、name 不能为空,不能以数字或下划线开头,尽量不使用特殊字符等。
2、partition_num 指定表空间数据分片数量,不同的分片可分布在不同的机器,来避免单台机器的资源限制。
3、replica_num 副本数量,建议设置成3,表示每个分片数据有两个备份,保证数据高可用。
engine配置:
字段标识 | 字段含义 | 类型 | 是否必填 | 备注 |
---|---|---|---|---|
name | 引擎名称 | string | 是 | 目前固定为gamma |
index_size | 分片索引阀值 | int | 否 | |
id_type | 唯一主键类型 | string | 否 | |
retrieval_type | 检索模型 | string | 是 | |
retrieval_param | 检索模型参数配置 | json | 否 |
1、name 不能为空,不能以数字或下划线开头,尽量不使用特殊字符等。
2、index_size 指定每个分片的记录数量达到多少开始创建索引,不指定则不创建索引。
3、max_size 指定每个分片最多存储的记录数量,防止服务器内存占用过多。
4、id_type 指定唯一记录主键类型,支持String和Long(定义为Long可节省内存占用)。
5、retrieval_type 检索模型,目前支持六种类型,IVFPQ,HNSW,GPU,IVFFLAT,BINARYIVF,FLAT,不同的检索模型需要的参数配置及默认值如下:
IVFPQ:
字段标识 | 字段含义 | 类型 | 是否必填 | 备注 |
---|---|---|---|---|
ncentroids | 聚类中心数量 | int | 否 | 默认2048 |
nsubvector | PQ拆分子向量大小 | int | 否 | 默认64, 值为4的倍数 |
"retrieval_type": "IVFPQ",
"retrieval_param": {
"ncentroids": 2048,
"nsubvector": 64
}
HNSW:
字段标识 | 字段含义 | 类型 | 是否必填 | 备注 |
---|---|---|---|---|
nlinks | 节点邻居数量 | int | 否 | 默认32 |
efConstruction | 构图时寻找节点邻居过程中在图中遍历的深度 | int | 否 | 默认40 |
"retrieval_type": "HNSW",
"retrieval_param": {
"nlinks": 32,
"efConstruction": 40
}
注意: 1、向量存储只支持MemoryOnly
2、创建索引不需要训练,index_size 值大于0均可
GPU(针对GPU编译版本):
字段标识 | 字段含义 | 类型 | 是否必填 | 备注 |
---|---|---|---|---|
ncentroids | 聚类中心数量 | int | 否 | 默认2048 |
nsubvector | PQ拆分子向量大小 | int | 否 | 默认64, 值为4的倍数 |
"retrieval_type": "GPU",
"retrieval_param": {
"ncentroids": 2048,
"nsubvector": 64
}
IVFFLAT:
字段标识 | 字段含义 | 类型 | 是否必填 | 备注 |
---|---|---|---|---|
ncentroids | 聚类中心数量 | int | 否 | 默认256 |
"retrieval_type": "IVFFLAT",
"retrieval_param": {
"ncentroids": 256
}
注意: 1、向量存储方式只支持RocksDB
BINARYIVF:
字段标识 | 字段含义 | 类型 | 是否必填 | 备注 |
---|---|---|---|---|
ncentroids | 聚类中心数量 | int | 否 | 默认256 |
"retrieval_type": "BINARYIVF",
"retrieval_param": {
"ncentroids": 256
}
注意: 1、向量长度是8的倍数
properties配置:
1、表空间结构定义字段支持的类型(即type的值)有4种: keyword,integer,float,vector(keyword等价于string)。
2、keyword类型的字段支持index、array属性,index定义是否创建索引,array指定是否允许多个值。
3、integer,float类型的字段支持index属性,index设为true的字段支持使用数值范围过滤查询。
4、vector 类型字段为特征字段,一个表空间中支持多个特征字段,vector类型的字段支持的属性如下:
字段标识 | 字段含义 | 类型 | 是否必填 | 备注 |
---|---|---|---|---|
dimension | 特征维数 | int | 是 | |
format | 归一化处理 | string | 否 | 设置为normalization对添加的特征向量归一化处理 |
store_type | 特征存储类型 | string | 否 | 支持MemoryOnly、Mmap和RocksDB, 默认MemoryOnly |
store_param | 存储参数设置 | json | 否 | 针对不同store_type的存储参数 |
model_id | 特征插件模型 | string | 否 | 使用特征插件服务时指定 |
5、dimension 定义type是vector的字段,指定特征维数大小。
6、store_type 特征向量存储类型,有以下三个选项:
“MemoryOnly”:原始向量都存储在内存中,存储数量的多少受内存限制,适用于数据量不大(千万级),对性能要求高的场景
“RocksDB”:原始向量存储在RockDB(磁盘)中,存储数量受磁盘大小限制,适用单机数据量巨大(亿级以上),对性能要求不高的场景
“Mmap”:原始向量存储在磁盘文件中,存储数量受磁盘大小限制,适用单机数据量巨大(亿级以上),对性能要求不高的场景
7、store_param 针对不同store_type的存储参数,其包含以下两个子参数。
cache_size: 数值类型,单位是M bytes,默认1024。store_type=”RocksDB”时,它表示RocksDB的读缓冲大小,值越大读向量的性能越好,一般设置1024、2048、4096和6144即可;store_type=”Mmap”时,它表示写缓冲的大小,不用太大,一般512、1024或2048即可;store_type=”MemoryOnly”,它没有用。
compress: bool类型,默认false。true表示对原始向量进行压缩,一般会将原始向量压缩为原来的50%,可以节省内存和磁盘;false表示不压缩;目前支持RocksDB和MemoryOnly, Mmap暂不支持。
查看表空间¶
curl -XGET http://master_server/space/$db_name/$space_name
删除表空间¶
curl -XDELETE http://master_server/space/$db_name/$space_name
数据操作¶
http://router_server 代表router服务,$db_name是创建的库名, $space_name是创建的空间名, $id是数据记录的唯一id.
单条插入¶
插入时不指定唯一标识id
curl -XPOST -H "content-type: application/json" -d'
{
"field1": "value1",
"field2": "value2",
"field3": {
"feature": [0.1, 0.2]
}
}
' http://router_server/$db_name/$space_name
field1和field2是标量字段,field3是特征字段。所有字段名、值类型和定义表结构时保持一致。
返回值格式如下:
{
"_index": "db1",
"_type": "space1",
"_id": "AW5J1lNmJG6WbbCkHrFW",
"status": 200
}
其中_index 库名称, _type 表空间名称,_id 是服务端生成的记录唯一标识,可以由用户指定,对数据的修改和删除需要使用该唯一标识。
插入时指定唯一标识
curl -XPOST -H "content-type: application/json" -d'
{
"field1": "value1",
"field2": "value2",
"field3": {
"feature": [0.1, 0.2]
}
}
' http://router_server/$db_name/$space_name/$id
$id 是插入数据时使用指定的值替换服务端生成的唯一标识,$id值不能使用url路径等特殊字符。若库中已存在该唯一标识的记录则覆盖。
批量插入¶
curl -H "content-type: application/json" -XPOST -d'
{"index": {"_id": "v1"}}\n
{"field1": "value", "field2": {"feature": []}}\n
{"index": {"_id": "v2"}}\n
{"field1": "value", "field2": {"feature": []}}\n
' http://router_server/$db_name/$space_name/_bulk
json格式的变体,{“index”: {“_id”: “v1”}} 指定记录的id, _id值为空后台自动生成唯一id, {“field1”: “value”, “field2”: {“feature”: []}} 指定插入的数据,每行json字符串均以\n结尾。
更新¶
更新时必须指定唯一标识id
curl -H "content-type: application/json" -XPOST -d'
{
"field1": "value1",
"field2": "value2",
"field3": {
"feature": [0.1, 0.2]
}
}
' http://router_server/$db_name/$space_name/$id/_update
请求路径中指定唯一标识$id,使用指定id插入的方式进行数据覆盖更新(后续可支持单个字段修改)。
删除¶
根据唯一id标识删除数据
curl -XDELETE http://router_server/$db_name/$space_name/$id
根据查询过滤结果删除数据
curl -H "content-type: application/json" -XPOST -d'
{
"query": {
"sum": [{}]
}
}
' http://router_server/$db_name/$space_name/_delete_by_query
查询详细语法见下文
查询¶
查询示例
curl -H "content-type: application/json" -XPOST -d'
{
"query": {
"sum": [{
"field": "field_name",
"feature": [0.1, 0.2, 0.3, 0.4, 0.5],
"min_score": 0.9,
"boost": 0.5
}],
"filter": [{
"range": {
"field_name": {
"gte": 160,
"lte": 180
}
}
},
{
"term": {
"field_name": ["100", "200", "300"],
"operator": "or"
}
}]
},
"retrieval_param": {
"nprobe": 20
},
"fields": ["field1", "field2"],
"is_brute_search": 0,
"online_log_level": "debug",
"quick": false,
"vector_value": false,
"client_type": "leader",
"l2_sqrt": false,
"sort": [{"field1":{"order": "asc"}}],
"size": 10
}
' http://router_server/$db_name/$space_name/_search
查询参数整体json结构如下:
{
"query": {
"sum": [],
"filter": []
},
"retrieval_param": {"nprobe": 20},
"fields": ["field1", "field2"],
"is_brute_search": 0,
"online_log_level": "debug",
"quick": false,
"vector_value": false,
"client_type": "leader",
"l2_sqrt": false,
"sort": [{"field1":{"order": "asc"}}],
"size": 10
}
参数说明:
字段标识 | 类型 | 是否必填 | 备注 |
---|---|---|---|
sum | json数组 | 是 | 查询特征 |
filter | json数组 | 否 | 查询条件过滤: 数值过滤 + 标签过滤 |
fields | json数组 | 否 | 指定返回那些字段, 默认只返回唯一id和分值 |
is_brute_search | int | 否 | 默认0 |
online_log_level | string | 否 | 值为debug, 开启打印调试日志 |
quick | bool | 否 | 默认false |
vector_value | bool | 否 | 默认false |
client_type | string | 否 | 默认leader |
ivf_flat | bool | 否 | 默认false,仅适用于IVFPQ模型,结果开根号 |
sort | json数组 | 否 | 指定字段排序(只针对匹配结果,非整体) |
size | int | 否 | 指定返回结果数量,默认50 |
retrieval_param 参数指定模型计算时的参数,不同模型支持的参数不同,如下示例:
- metric_type: 计算类型,支持InnerProduct和L2, 默认L2。
- nprobe: 搜索桶数量。
- recall_num: 召回数量,默认等于查询参数中size的值,设置从索引中搜索数量,然后计算size个最相近的值。
- parallel_on_queries: 默认1, 搜索间并行;0代表桶间并行。
- efSearch: 图遍历的距离。
IVFPQ:
"retrieval_param": {
"parallel_on_queries": 1,
"recall_num" : 100,
"nprobe": 80,
"metric_type": "L2"
}
GPU:
"retrieval_param": {
"recall_num" : 100,
"nprobe": 80,
"metric_type": "L2"
}
HNSW:
"retrieval_param": {
"efSearch": 64,
"metric_type": "L2"
}
IVFFLAT:
"retrieval_param": {
"parallel_on_queries": 1,
"nprobe": 80,
"metric_type": "L2"
}
FLAT:
"retrieval_param": {
"metric_type": "L2"
}
- sum json结构说明:
"sum": [{
"field": "field_name",
"feature": [0.1, 0.2, 0.3, 0.4, 0.5],
"min_score": 0.9,
"boost": 0.5
}]
- sum 支持多个(对应定义表结构时包含多个特征字段)。
- field 指定创建表时特征字段的名称。
- feature 传递特征,维数和定义表结构时维数必须相同。
- min_score 指定返回结果中分值必须大于等于0.9,两个向量计算结果相似度在0-1之间,min_score可以指定返回结果分值最小值,max_score可以指定最大值。如设置: “min_score”: 0.8,“max_score”: 0.95 代表过滤0.8<= 分值<= 0.95 的结果。同时另外一种分值过滤的方式是使用: “symbol”:”>=”,”value”:0.9 这种组合方式,symbol支持的值类型包含: > 、 >= 、 <、 <= 4种,value及min_score、max_score值在0到1之间。
- boost指定相似度的权重,比如两个向量相似度分值是0.7,boost设置成0.5之后,返回的结果中会将分值0.7乘以0.5即0.35。
- filter json结构说明:
"filter": [
{
"range": {
"field_name": {
"gte": 160,
"lte": 180
}
}
},
{
"term": {
"field1": ["100", "200", "300"],
"operator": "or"
},
"term": {
"field2": ["a", "b", "c"],
"operator": "and"
},
"term": {
"field3": ["A1", "B2"],
"operator": "not"
}
}
]
- filter 条件支持多个,多个条件之间是交的关系。
- range 指定使用数值字段integer/float 过滤, filed_name是数值字段名称, gte、lte指定范围, lte 小于等于, gte大于等于,若使用等值过滤,lte和gte设置相同的值。上述示例表示查询field_name字段大于等于160小于等于180区间的值。
- term 使用标签过滤, field1是定义的标签字段,允许使用多个值过滤,可以求并“operator”: “or” , 求交: “operator”: “and”,不包含: “operator”: “not”。
- is_brute_search 0使用索引搜索(建完索引前查询结果为空), 1使用暴力搜索,默认值0。
- online_log_level 设置成”debug” 可以指定在服务端打印更加详细的日志,开发测试阶段方便排查问题。
- quick 搜索结果默认将PQ召回向量进行计算和精排,为了加快服务端处理速度设置成true可以指定只召回,不做计算和精排。
- vector_value 为了减小网络开销,搜索结果中默认不包含特征数据只包含标量信息字段,设置成true指定返回结果中包含原始特征数据。
- client_type leader,random,no_leader,默认leader仅从主数据节点查询,random: 从ps主从节点随机选择,no_leader:只查询从节点。
- size 指定最多返回的结果数量。若请求url中设置了size值http://router_server/$db_name/$space_name/_search?size=20优先使用url中指定的size值。
id查询¶
curl -XGET http://router_server/$db_name/$space_name/$id
批量id查询¶
curl -H "content-type: application/json" -XPOST -d'
{
"query": {
"ids": ["id1", "id2"],
"fields": ["field1"]
}
}
' http://router_server/$db_name/$space_name/_query_byids
ids指定多个id, fields 指定返回每条记录中那些字段
批量特征查询1¶
curl -H "content-type: application/json" -XPOST -d'
[{
"query": {
"sum": [{
"field": "vector_field_name",
"feature": [0.1, 0.2]
}]
}
},
{
"query": {
"sum": [{
"field": "vector_field_name",
"feature": [0.1, 0.2]
}]
}
}]
' http://router_server/$db_name/$space_name/_bulk_search
把多个单条查询的参数拼接成数组作为请求参数,返回结果和请求顺序保持一致。
批量特征查询2¶
curl -H "content-type: application/json" -XPOST -d'
{
"query": {
"sum": [{
"field": "vector_field_name",
"feature": [0.1, 0.2]
}]
}
}
' http://router_server/$db_name/$space_name/_msearch
适用于多个查询使用相同过滤条件的情况,将多个查询的特征拼接成一个特征数组(比如定义128维的特征,批量查询10条,则将10个128维特征按顺序拼接成1280维特征数组赋值给feature字段),后台接收到请求后按表结构定义的特征字段维度进行拆分,按顺序返回匹配结果。
根据id特征查询¶
curl -H "content-type: application/json" -XPOST -d'
{
"query": {
"ids": ["id1", "id2"]
},
"size": 10
}
' http://router_server/$db_name/$space_name/_query_byids_feature
传入记录id, 首先查询出该记录的特征,然后再用特征进行查询,返回匹配结果。
多向量查询¶
表空间定义时支持多个特征字段,因此查询时可以支持相应数据的特征进行查询。以每条记录两个向量为例:定义表结构字段
{
"field1": {
"type": "vector",
"dimension": 128
},
"field2": {
"type": "vector",
"dimension": 256
}
}
field1、field2均为向量字段,查询时搜索条件可以指定两个向量:
{
"query": {
"sum": [{
"field": "filed1",
"feature": [0.1, 0.2, 0.3, 0.4, 0.5],
"min_score": 0.9
},
{
"field": "filed2",
"feature": [0.8, 0.9],
"min_score": 0.8
}]
}
}
field1和field2过滤的结果求交集,其他参数及请求地址和普通查询一致。
GPU使用¶
注意事项¶
- 编译环境需要带GPU安装GUDA>=9.0.
- 编译前Vearch引擎CMakeLists.txt配置中BUILD_WITH_GPU设置为on.
- 创建表时gamm设置参数nprobe不大于1024(CUDA9.0)或者不大于2048(CUDA>=9.2), index_size设置为0.
- 建表时特征字段retrieval_type参数设置为GPU.
- 数据插入和建索引时不支持搜索。
- 数据不自动创建索引,调用curl -XPOST http://router_server/$db_name/$space_name/_forcemerge创建索引.
- 不支持实时添加数据到索引,新增数据只有重建索引后才会生效。(重建索引:curl -XPOST http://router_server/$db_name/$space_name/_forcemerge)
效果评估¶
基准
本文档显示了我们做的实验和得到的结果。我们做了两个系列的实验。首先,我们在单个节点上进行了基于faiss的改进的Vearch ivfpq模型的召回率实验。其次,基于Vearch 集群进行了实验。
我们使用检索返回的k个候选值(k∈{1,10,100})是否包含最近邻的结果来计算召回,其中标签通过暴力搜索获得。
实验数据会因为实现、不同机器等的变化而略有变化。
数据集
实验分别在128维SIFT特征和512维VGG特征上进行。
数据集SIFT1M
可以从如下网址下载ANN_SIFT1M
http://corpus-texmex.irisa.fr/
数据集VGG1M 和 VGG10M
分别收集100万和1000万的图片数据提取VGG特征得到VGG1M (100万)和VGG10M(1000万),其中VGG1M 和VGG10M并不相关。
数据集VGG100M , VGG500M 和 VGG1B
另外收集了10亿图片数据来构建VGG100M(1亿) , VGG500M(5亿) 和 VGG1B(10亿)。
Nprobe 实验
实验分别在SIFT1M, VGG1M 和 VGG10M上进行。其中ncentroids =256,nbytes = 32,nprobe ∈{1,5,10,20,30,40,50,80,100,200}。图中数据为recall@1结果。
结果

可以看到当nprobe超过25后,召回基本上没有明显的变化了。
Ncentroids 实验
实验在VGG10M进行。其中nprobe = 50,nbytes = 32, ncentroids∈{64,128,256,512,1024,2048,4096,8192} 。图中数据为recall@1结果。
结果

可以看到召回基本不随ncentroids变化而变化,但是ncentroids越大,QPS越高。
Nbytes 实验
实验在VGG10M进行。其中nprobe = 50,ncentroids = 256, nbytes ∈{4,8,16,32,64}。图中数据为recall@1结果。
结果

当nbytes越大,召回越高,当然QPS随之降低。
对比实验
实验在 SIFT1M, VGG1M 和 VGG10M 上进行,并与faiss中的一些模型进行对比。
模型参数
表格中参数为空,则对应模型不包含该参数。其中 links, efSearch 和 efConstruction 为 faiss 中的 hnsw 定义的参数。
model | ncentroids | nprobe | bytes of SIFT | bytes of VGG | links | efSearch | efConstruction |
---|---|---|---|---|---|---|---|
pq | | 32 | 64 | |||||
ivfpq |256 | 20 | 32 | 64 | ||||
imipq | 2^(2*10) | 2048 | 32 | 64 | |||
opq+pq | 32 | 64 | |||||
hnsw | 32 | 64 | 40 | ||||
ivfhnsw | 256 | 20 | 32 | 64 | 40 | ||
Vearch | 256 | 20 | 32 | 64 |
结果
SIFT1M的召回:
model | recall@1 | recall@10 | recall@100 |
---|---|---|---|
pq | 0.6274 | 0.9829 | 0.9999 |
ivfpq | 0.6167 | 0.9797 | 0.9960 |
imipq | 0.6595 | 0.9775 | 0.9841 |
opq+pq | 0.6250 | 0.9821 | 1.0000 |
hnsw | 0.9792 | 0.9867 | 0.9867 |
ivfhnsw | 0.9888 | 0.9961 | 0.9961 |
Vearch | 0.9581 | 0.9645 | 0.9645 |
VGG1M的召回 :
model | recall@1 | recall@10 | recall@100 |
---|---|---|---|
pq | 0.5079 | 0.8922 | 0.9930 |
ivfpq | 0.4985 | 0.8792 | 0.9704 |
imipq | 0.5077 | 0.8618 | 0.9248 |
opq+pq | 0.5213 | 0.9105 | 0.9975 |
hnsw | 0.9496 | 0.9550 | 0.9551 |
ivfhnsw | 0.9690 | 0.9744 | 0.9745 |
Vearch | 0.9536 | 0.9582 | 0.9585 |
VGG10M的召回 :
model | recall@1 | recall@10 | recall@100 |
---|---|---|---|
pq | 0.5842 | 0.8980 | 0.9888 |
ivfpq | 0.5913 | 0.8896 | 0.9748 |
imipq | 0.5925 | 0.8878 | 0.9570 |
opq+pq | 0.6126 | 0.9160 | 0.9944 |
hnsw | 0.8877 | 0.9069 | 0.9074 |
ivfhnsw | 0.9638 | 0.9839 | 0.9843 |
Vearch | 0.9272 | 0.9464 | 0.9468 |
集群实验¶
集群实验分别对 VGG100M , VGG500M 和 VGG1B进行实验,并添加是否过滤来进行实验,其中过滤是指在搜索的时候指定过滤条件来缩小搜索范围。VGG100M 搭建了 3 个masters, 3个 routers 和5 个 partition services 的集群。 VGG500M搭建了 3 个masters, 3个 routers 和24个 partition services 的集群。VGG1B搭建了 3 个masters, 6个 routers 和48 个 partition services 的集群。
结果

可以看到当average latency超过一定程度,QPS就不再发生明显变化了。
HNSW性能评测¶
data_size = 10M, 参数为 InnerProduct, nlinks = 32, efConstruction = 40, efSearch = 64
集群 | 机器配置 | 数据维度 | 构建索引时间 |
---|---|---|---|
masterrouterps各一个,均在一台机器上 | 56核256G内存 | 128 | 28min |
masterrouterps各一个,均在一台机器上 | 56核256G内存 | 512 | 33min10s |
masterrouterps各一台,且在不同机器上 | 8核16G内存 | 128 | 2h38m |
内存使用率
以8核16G机器为样本进行分析,vearch ps共使用内存16G * 0.627 = 10.032G,其中原始数据特征大小为10M * 128 * 4 ~ 5G,其它为索引以及正排字段占用内存。使用时仅创建一个正排字段和向量字段。

10M数据, 机器配置56核256G,add time = 28min01s, 左侧是构建索引CPU利用率,右侧是搜索时CPU利用率

model | 返回结果数 | 并发数 | QPS | tp99 | tp100 |
---|---|---|---|---|---|
HNSW | 100 | 2000 | 5069.48 | 959 | 1944 |
HNSW | 100 | 1000 | 4680.62 | 521 | 994 |
HNSW | 100 | 100 | 4508.33 | 49 | 86 |
HNSW | 100 | 50 | 4146.55 | 22 | 35 |
HNSW | 100 | 10 | 2048.43 | 8 | 117 |
HNSW | 100 | 1 | 182.74 | 9 | 160 |
10M数据,机器配置56核256G,d = 512, add time = 33min10s,左侧是构建索引CPU利用率,右侧是搜索时CPU利用率

model | 返回结果数 | 并发数 | QPS | tp99 | tp100 |
---|---|---|---|---|---|
HNSW | 100 | 2000 | 4194.72 | 1181 | 2650 |
HNSW | 100 | 1000 | 4058.59 | 628 | 1059 |
HNSW | 100 | 100 | 3784.56 | 57 | 172 |
HNSW | 100 | 50 | 3723.75 | 24 | 43 |
HNSW | 100 | 10 | 1248.46 | 15 | 1208 |
HNSW | 100 | 1 | 142.70 | 12 | 71 |
10m数据,机器配置8核16G,d = 128, add_time = 2h38m
构建索引时CPU使用率基本保持在100%, 搜索时cpu使用率,当并发数为50,100时,可以看到cpu使用率已经100%,故不做并发数1000/2000的对比实验

model | 返回结果数 | 并发数 | QPS | tp99 | tp100 |
---|---|---|---|---|---|
HNSW | 100 | 100 | 888.25 | 279 | 402 |
HNSW | 100 | 50 | 1126.06 | 95 | 199 |
HNSW | 100 | 10 | 918.50 | 15 | 31 |
HNSW | 100 | 1 | 91.74 | 14 | 25 |
召回评测使用sift1M
model | parameters | recall@1 | recall@10 | recall@100 |
---|---|---|---|---|
HNSW | InnerProduct, nlinks = 32, efConstruction = 40, efSearch = 64 | 0.9769 | 0.9852 | 0.9852 |
GPU版性能¶
服务器配置
hardware | config |
---|---|
CPU | E5-2683 v4 16cores |
memory | 16G |
GPU | Tesla P40 |
数据:128维float数据,总插入数据量2亿
性能:并发20查询,QPS达到3000,tp99在30ms以内
常见问题¶
- Vearch的向量搜索引擎gamma基于faiss实现,faiss版本有较大改动不兼容历史版本时Vearch可能编译不成功。