1. 程式人生 > 實用技巧 >向量儲存和檢索解決方案--Vearch

向量儲存和檢索解決方案--Vearch

1、摘要

vearch是對大規模深度學習向量進行高效能相似搜尋的向量檢索資料庫,可以輕鬆應對海量的向量資料的儲存和檢索。(原始碼地址:https://github.com/vearch/vearch,文件地址:https://vearch.readthedocs.io/zh_CN/latest/。)

vearch部署架構簡單,提供RESTful Api 操作,使用起來非常方便。使用vearch作為向量檢索服務,演算法工程師可以更加專注於研究演算法效果的提升。

下面通過vearch 系統架構,功能特性,系統部署,系統應用,這幾個方面進行介紹,從中會穿插介紹vearch的一些高階特性,旨在使用者能夠輕鬆構建自己的vearch服務,對vearch有一個全面的認識和了解。

2、系統架構

vearch 分為3個模組,其中包括 master、router和PartitionServer(簡稱ps)。

Master: 負責元資料管理和叢集資源協調。

Router: 提供RESTful API: create、delete、search、update; 請求路由轉發及結果合併。

PartitionServer(PS): 基於raft複製的文件分片; Gamma向量搜尋引擎,它提供了儲存、索引和檢索向量、標量的能力。

Architecture

3、功能特性

RESTful API ;

資料持久化儲存,高可靠性;

高可用;

高效能;

高召回率;

4、系統部署

vearch 部署便捷,我們常用的方式,可以採用容器化的方式進行快速部署,這裡提供兩種部署架構,採用docker容器化和k8s的方式都可以實現。

4.1、部署架構

a、快速分散式部署

b、etcd+vearch分散式部署

如果你對etcd比較熟悉,或者有現成的etcd服務,那可以使用b的部署架構(後續會提供完整文件),ps和router可通過域名的方式進行部署。

為了操作簡單,下面我們介紹一下基於docker容器化,a的快速分散式部署方案:

採用一臺物理機,建立多個docker容器。現在我們需要部署節點為:master 1個、router 1個、ps 3個,總共5個節點,即建立5個docker容器。如果你採用的是多臺物理機搭建docker環境,需要注意容器之間的連通性,ip地址和埠的配置。

4.2、docker容器化部署

4.2.1、啟動vearch容器

首先我們需要下載vearch 的docker 映象。

執行:

docker pull vearch/vearch:v3.2.0

執行docker images 檢視映象是否存在:找到vearch v3.2.0

修改配置檔案:(可以使用docker固定IP的方式,需要提前知道 master的ip地址,假設我們這裡保證master的ip 為172.17.0.2),修改後的配置檔案如下。

[global]

# the name will validate join cluster by same name

name ="vdb"

# you data save to disk path ,If you are in a production environment, You'd better set absolute paths

data = [

"/export/vdb/baud/datas/",

]

# log path , If you are in a production environment, You'd better set absolute paths

log ="/export/vdb/baud/logs/"

#defaultlog typeforany model

level ="debug"

# master <-> ps <-> router will usethiskey to send or receive data

signkey ="vdb"

# skip authformaster and router

skip_auth =true

# tell Vearch whether it should manage it's own instance of etcd or not

self_manage_etcd =false

# automatically remove the failed node and recover whennewnodes join

auto_recover_ps =false

# support access etcd basic auth,depend on self_manage_etcd =true

support_etcd_auth =false

[etcd]

#etcd server ip or domain

address = ["my.vearch.local"]

# advertise_client_urls AND listen_client_urls

etcd_client_port =2379

#ifyou are master you'd better set all configforrouter and ps and router and ps usedefaultconfig it so cool

[[masters]]

#name machine nameforcluster

name ="master1"

#ip or domain

address ="172.17.0.2"

# api portforhttp server

api_port =7810

# portforetcd server

etcd_port =2378

# listen_peer_urls List of comma separated URLs to listen onforpeer traffic.

# advertise_peer_urls List ofthismember'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 ofthismember's client URLs to advertise to thepublic.

# The URLs needed to be a comma-separated list.

# advertise_client_urls AND listen_client_urls

etcd_client_port =2370

data = ["/export/vdb/vdb-master/datas/"]

[router]

# portforserver

port =8230

fix_name =0

pprof_port =6061

monitor_port =8818

[ps]

# portforserver

rpc_port =8081

#raft config begin

raft_heartbeat_port =8898

raft_replicate_port =8899

heartbeat-interval =200#ms

raft_retain_logs =1000000

raft_replica_concurrency =1

raft_snap_concurrency =1

journal_enabled =true

max_size =50000000000

pprof_port =6060

engine_dwpt_num =1

raft_truncate_count =1000000000

執行下面的命令,建立docker容器。說明:master1:7810埠是master restful 埠號,包括建立表和查詢的一些功能介面,2370是etcd埠號,可以使用etcdctl工具查詢元資料資訊。

nohupdocker run -p 7810:7810 --name master1 -v$PWD/config.toml:/vearch/config.toml vearch/vearch:3.2.0 master > master.log 2>&1 &

nohupdocker run -p 8230:8230 --name router1 -v$PWD/config.toml:/vearch/config.toml vearch/vearch:3.2.0 router > router.log 2>&1 &

nohupdocker run --name ps1 -v$PWD/config.toml:/vearch/config.toml vearch/vearch:3.2.0ps> ps1.log 2>&1 &

nohupdocker run --name ps2 -v$PWD/config.toml:/vearch/config.toml vearch/vearch:3.2.0ps> ps2.log 2>&1 &

nohupdocker run --name ps3 -v$PWD/config.toml:/vearch/config.toml vearch/vearch:3.2.0ps> ps3.log 2>&1 &

如下圖:

5個節點都啟動成功。

4.2.2、環境驗證

a、執行:curl localhost:7810(保證docker埠對映成功),或者 啟動另外一個centos docker環境。

啟動 另外一個centos 容器:docker run -it centos:centos7 ,執行:curl 172.17.0.2:7810,返回叢集狀態資訊,說明vearch master啟動成功。

b、叢集整體驗證:如下,master的ip地址和埠號為172.17.0.2:7810。

執行:curl172.17.0.2:7810/list/server

獲取叢集全部的ps節點資訊。

得到json結果,如下:

執行完以上步驟,那麼vearch叢集就已經搭建完成了。

4.3、k8s 環境部署

後續會提供完整文件,可先根據前面的docker環境部署進行探索。

5、功能驗證

從系統應用的角度,把基本功能驗證流程走一遍(詳細的功能API文件參考:https://vearch.readthedocs.io/zh_CN/latest/)。功能驗證的同時,穿插加入高可用和擴容、縮容的介紹。

資料來源於真實業務場景歷史資料,使用者在測試使用時,需要根據業務需求進行合理的選擇。

這裡指定兩個變數:

a、master_server指的是master服務的ip地址和埠號[masterIP:7810]。為了方便操作,可以將docker容器的埠對映到物理機,此時masterIP指的就是物理宿主機ip。

b、router_server指的是router服務的IP地址和埠號[routerIP:8230]。,此時routerIP指的也是物理宿主機ip。

5.1、建表

a、建庫

curl -H"content-type: application/json"-XPUT -d'

{

"name":"ts_db_1"

}

' http://172.17.0.2:7810/db/_create

b、建表 :16 維向量,檢索型別:IVFPQ 1個分割槽,3個副本。

curl -XPUT -H"content-type: application/json"-d'

{

"name":"ts_space_1",

"partition_num":1,

"replica_num":3,

"engine": {

"name":"gamma",

"index_size":70000,

"id_type":"String",

"retrieval_type":"IVFPQ",

"retrieval_param": {

"metric_type":"InnerProduct",

"ncentroids":256,

"nsubvector":32

}

},

"properties": {

"feature": {

"dimension":16,

"type":"vector"

},

"image_url": {

"index":true,

"type":"string"

}

}

}

' http://172.17.0.2:7810/space/ts_db_1/_create

驗證一下是否建立成功:

curl http://172.17.0.2:7810/space/ts_db_1/ts_space_1

5.2、資料插入

5.2.1、指定docId[可根據id查詢doc] ,(檢視,確認一下router1 的ip地址 docker inspect <router 1 的docker id>,此處為 172.17.0.3)

curl -XPOST -H"content-type: application/json"-d'

{"feature": {"feature":[0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4,1.5]},"image_url":"5e7de70eE1d0e2912268d0db90558041d.jpg"}

' http://172.17.0.3:8230/ts_db_1/ts_space_1/5e7de70eE1d0e2912268d0db90558041d.jpg

5.2.2、不指定docId

curl -XPOST -H"content-type: application/json"-d'

{"feature": {"feature":[0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4,1.5]},"image_url":"5e7de70eE1d0e2912268d0db90558041d.jpg"}

' http://172.17.0.3:8230/ts_db_1/ts_space_1

返回結果:

{"_index":"ts_db_1","_type":"ts_space_1","_id":"5e7de70eE1d0e2912268d0db90558041d.jpg","status":200}

可根據status判斷資料是否插入成功。status = 200代表成功。

5.2.3、資料正確性

資料插入成功,可通過以下方式檢驗資料正確性:

a、根據插入成功資料量,插入成功的資料量和系統的資料量一致。插入成功的資料量,使用者可自己統計記錄,系統資料量查詢方式:可查詢partition每個副本包含的資料量。

curl172.17.0.2:7810/_cluster/health

資料格式如下:

Tips:資料插入過程,會出現丟資料的情況嗎?

vearch採用raft 來保證資料的一致性和正確性。raft commit 資料時,已經將資料寫入到了多數的節點上,才會返回成功,即使leader出現故障,也會重新選舉出擁有最新資料的節點作為leader繼續進行資料的寫入。不會出現丟失資料的情況。

b、插入資料按照指定docId進行(在下一節資料查詢中介紹方法),隨機抽取資料的docId,查詢系統是否正確返回資料。

c、觀察3個副本,每個副本的資料量是否保持一致。

5.3、資料查詢

a、根據id查詢:

curl -XGET http://172.17.0.3:8230/ts_db_1/ts_space_1/5e7de70eE1d0e2912268d0db90558041d.jpg

b、向量檢索:

curl -H"content-type: application/json"-XPOST -d'

{

"query": {

"sum": [{

"field":"feature",

"feature": [0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4,1.5],

"boost":1,

"min_score":0.0,

"max_score":1.0

}]

},

"size":1

}

' http://172.17.0.3:8230/ts_db_1/ts_space_1/_search

5.4、高可用

vearch採用多副本的模式,每個partition都可以建立多個副本,只要系統中的副本存活數大於1/2,既可以保證系統的可用性。

例子:我們上面建立的庫表 [ts_db_1/ts_space_1] ,1個partition建立3個副本;partitionID 1,replicasIDs[1,2,3]。leader 的數值是1,代表server 為1的節點,現在是partitionID為1的raft組的leader。

可以通過:curl172.17.0.2:7810/list/server 檢視。也可通過 etcdctl 檢視元資料。

高可用場景的驗證方法:

a、partition 的某個非leader副本故障:leader正常,資料的寫入是正常的;如果該副本,經過一段時間後,重啟成功,該副本會自動同步leader節點的資料至恢復到最新資料。如果副本故障,無法重啟恢復,可通過擴容增加一個副本(參見擴容、縮容)。

故障的副本,可以通過 :curl 172.17.0.2:7810/_cluster/health 驗證,資料量是否恢復一致。(參見:資料正確性)

b、leader副本故障:leader節點故障,會出現短暫的資料無法寫入,秒級時間會重新選出新的leader,資料寫入會恢復正常,故障的副本也可重啟恢復或擴容恢復。

副本故障,不斷的寫入資料,觀察資料的返回狀態,1、統計插入成功的資料量與系統資料量是否一致;(正確性驗證)2、觀察非leader副本故障,資料的寫入和讀取是否正常(正常);3、觀察leader副本故障,資料的寫入讀取是否正常(秒級 leader切換,資料短暫不可寫,很快會恢復)。

5.5、擴容、縮容

回顧一下,我們現在是總共5個節點 3個ps節點,假如系統流量高峰,需要擴容,或者高峰過後需要縮容,可以通過下面的方法進行ps節點擴容。(router節點擴容,直接增加新的節點即可)

5.5.1、擴容

擴容,首先我們需要增加新的節點加入叢集。

a、nohupdocker run --name ps4 -v$PWD/config.toml:/vearch/config.toml vearch/vearch:3.2.0ps> ps4.log 2>&1 &

執行:curl 172.17.0.2:7810/list/server,檢視新增節點的nodeId,partitions資訊為空。

可以看到,新增的節點,node_id為4。

執行建立副本命令,說明:method = 0代表新增副本,method = 1代表減少副本。node_id為需要操作的節點id。

curl -XPOST -H"content-type:application/json"-d '{

"partition_id":1,

"node_id":4,

"method":0

}

' http://172.17.0.2:7810/partition/change_member

再次通過curl 172.17.0.2:7810/list/server查詢。

5.5.2、縮容

縮容操作命令為:

curl -XPOST -H"content-type:application/json"-d '{

"partition_id":1,

"node_id":4,

"method":1

}

' http://172.17.0.2:7810/partition/change_member

其他的查詢命令同擴容一致。

6、總結

vearch提供了完善的分散式環境下的向量儲存和檢索服務。內容從容器化部署,到實際應用場景都進行了詳細的介紹。同時還介紹了資料正確性的驗證方法,高可用的驗證方法,副本的擴容和縮容方法以及高召回率。服務恢復釋出,版本升級,也不會影響服務的查詢效能。

下表為一些相關特性總結:

硬體環境

資料正確性

高可用

擴容、縮容

高召回率

備註

docker 環境:

1 router

硬體指標:4c,8G

3 ps

硬體指標:16c,32G

與插入成功的資料量一致,不存在資料丟失。

支援多副本,副本故障數小於副本總數的1/2,保證系統的百分之百可用性。很好的支援副本的擴容和縮容。支援多種索引,有高召回率。

召回測試

測試方法

方法說明:用vearch的hnsw召回模型與暴力搜尋在公開人臉資料集上做召回對比。

資料集:VGGFace2的300萬人臉資料集

特徵模型:Inception-resnet , 提取的特徵向量維度為512

測試資料:從300萬人臉特徵向量中隨機抽取1萬特徵向量做為搜尋測試資料

建庫資料:除去1萬測試資料,剩下的299萬人臉特徵向量全部插入vearch進行建庫

測試結果

召回模型識別總次數識別成功次數識別準確率相對暴力搜尋召回率
暴力搜尋10000965896.58%100%
HNSW10000963096.30%99.71%=9630/9658

結論:300萬數量級時,HNSW相比暴力搜尋的召回率為99.71%

效能測試

測試說明:效能測試仍然是向vearch中插入299萬基於VGGFace2人臉資料集的特徵向量,然後測試search的效能

測試部署:vearch所有服務都部署在 docker(k8s)平臺上

master:1臺4c8g

router:3臺 8c8g (避免router成為效能瓶頸,所以多部署了兩臺)

ps:1臺 8c16G (叢集資源核心)

測試結果

召回模型併發QPS平均latencytp99cpumemory
暴力搜尋12.5385ms540ms800%7.6G
HNSW15715ms20ms23%7.95G
暴力搜尋30nonenonenonenonenone
HNSW30130919ms75ms800%8G

備註:表中的“none”表示沒有測試的意義,放棄測試

結論:在相同的硬體資源情況下,HNSW的搜尋效能是暴力搜尋的>100倍