Milvus探究與壓測分析
1、背景
最近用到了向量搜索,所以要對milvus進行壓測。同時為了更加深入分析壓測中遇到的問題,也對milvus的部分源碼與文檔進行了走讀。其中遇到了一些問題與疑惑,我們也直接與milvus社區或開源貢獻者溝通。
通過壓測,我們發現某場景下存在milvus的性能提升不上去的問題,并給出基于該場景的解決方案,社區反饋給milvus官方。
以下為milvus的設計與壓測中遇到的一些問題與解決或跟進方案。
2、向量搜索與milvus
2.1 向量搜索
向量搜索簡稱ANNS,英文全名:Approximate Nearest Neighbor Search 。大致概念是從一堆向量中找出與某個目標向量距離最近的N個向量。最簡單粗暴的搜索方式是暴力搜索,但是可以通過擴增索引的方式加快搜索速度,提升大規模查詢的QPS。
當前的向量索引可以分為四大類:基于樹的索引、基于圖的索引、基于哈希的索引、基于量化的索引。其中圖思路由于較高的召回率、較好的性能和較多的后期優化思路,脫穎而出。
2.2 milvus
milvus(主要針對2.0以上版本)是一款元原生向量數據庫,支持向量的插入,ANNS搜索等。Milvus能夠很好地應對海量向量數據,它集成了目前在向量相似性計算領域比較知名的幾個開源庫(Faiss, SPTAG等),通過對數據和硬件算力的合理調度,以獲得最優的搜索性能。
官網:https://milvus.io/
3、milvus架構介紹
3.1 milvus的數據集概念
數據集概念:
- collection: 數據集,類似于mysql的表。
- channel:基于主鍵把數據集細分為很多channel,在數據寫入與查詢時,對應與msg borker中的通道的概念。
- partion:partion把數據集進行了一層劃分,常見的partion,比如:日期,按照日期分類存儲數據。partion與channel是正交的關系。
- segment:milvus數據集的最小單位,具體的索引都是基于segment去構建的。查詢的最小單位也是segment。
官網給出數據集讀取寫入例子:https://milvus.io/docs/v2.1.x/example_code.md
數據集查詢工具:https://github.com/milvus-io/birdwatcher
以下利用birdwatcher展示collection與segment信息:
以上輸出結果,對該工具進行了改造。
具體一個segment在etcd中所有的信息:
、
3.2 milvus架構圖
milvus官網給出的架構圖如下:
按照分組又可以分為以下幾大類別:
簡單介紹下以上各個微服務的功能:
- 系統門面:
- Proxy:所有SDK查詢都會經過proxy,proxy會把寫數據投遞到message borker中對應不同的channel,proxy會處理三類數據:寫請求,讀請求,控制類請求。
- 系統協調者:
- RootCoord:類似于傳統master角色 主要做一些DDL,DCL的管理,比如:創建Collection,刪除Collection,或對于partion做管理。此外還有一個更大的責任,rootCoord給系統分配全局唯一時間戳。
- 寫數據:
- DataCoord:協調者,分配,管理segment,管理dataNode,處理dataNode的故障恢復等。
- DataNode:消費來自數據流的數據,進行數據序列化,負責把log數據轉成log snapshot,刷新到磁盤中。
- 索引創建:
- IndexCoord:對sealed SegMent創建索引,管理indexNode。
- IndexNode:負責具體的索引創建事宜。
- 查詢:
- QueryCoord:數據查詢管理,負責管理QueryNode。
- QueryNode:負責具體的數據查詢事宜。
- 元信息與元數據存儲:
- MetaStore:metastore使用ETCD存儲。主要負責元信息與元數據的存儲。比如:表結構,Segment結構,全局時間戳等。
- 寫數據消息投遞:
- Log Borker:Log broker采用了pulsar。最新的2.0及以上版本中,寫入的數據都是先寫入Log Broker,然后DataNode從Log Broker中讀取。
- 數據與索引存儲:
- Object Store:Object store當前采用了minio,主要用來存儲數據,索引等。
以上可以看出微服務比較多,微服務之間的通信方式主要有以下幾種:
4、milvus向量寫入與讀取鏈路
4.1 milvus向量寫入路徑
- proxy通過produce把數據寫入到Message borker的物理channel中。
- DataNode作為消費者消費數據。
- DataNode定期把消費數據存到Object store中。
- DataNode會定期通知dataCoord記錄數據元信息。
3.2 milvus向量搜索路徑
以下當前最新版本2.1.4的讀流程 ,與網上的讀流程版本鏈路不同,應該是做了改造。
- Proxy收到向量搜索(ANNS)請求后,會把請求丟給shard leader query node。
- Leader querynode 會依據每個segments的分布,把ANNS請求分發給每個Query Node。Query Node內部會基于最小搜索單位Segment,cpu核數等去做并行查詢,并做結果reduce。
- Proxy收到所有的請求后,會對search結果做reduce,并返回給客戶端。
5、milvus壓測中的問題分析
壓測版本:milvus-2.1.4
數據維度:512dim
索引:
5.1 壓測結果
向量個數 | 索引 | 規格 | QPS | 99%耗時 |
十萬*512dim | FLAT | 2*(8cpu*16Gi) | 880 | 82ms |
十萬*512dim | FLAT | 2*(16cpu*16Gi) | 1489 | 62ms |
百萬*512dim | FLAT | 2*(16cpu*16Gi) | 240 | 200ms |
千萬*512dim | FLAT | 2*(16CPU*32Gi) | 20 | 1.98s |
5.2 壓測中遇到的問題與分析
QPS與CPU使用率壓不上去,CPU很難超過50%。(已經優化)
現象描述:
壓測過程中,發現QPS始終壓不上去,仔細排查發現查詢節點的cpu使用率上不去,導致qps也上不去。
- 解決方案:
初步懷疑是查詢節點調度問題,經過各種排查,發現與一個調度參數scheduler.cpuRation高度相關。以下是該參數在不同值的QPS情況。
規格 | scheduler.cpuRation | qps |
2*(8cpu*16Gi) | 20 | 385 |
2*(8cpu*16Gi) | 100 | 768 |
2*(8cpu*16Gi) | 120 | 913 |
2*(8cpu*16Gi) | 140 | 880 |
該參數主要用來評估一個search task的cpu使用情況,該參數越高,預示該task使用cpu越多,調度的時候,多個task去查詢的并行數量就會少一些。現在懷疑并行task太多,并不會達到很高的QPS。
milvus并沒有公開該參數配置,已經通過issue/enhancement提給milvus社區中了,后續版本應該會有所優化。
擴容查詢節點后,短時間內segments沒有自動均衡(懷疑,跟進中)
現象描述:
- 比如當前線上有兩個查詢節點,50個segments均分在兩個nodes上。壓測中多次發現如果增加一個node后,segments并不會自動均衡到新的node上。
- 當前進度:
整個壓測過程中做了三次寫入,有兩次沒有自動均衡,最后一次自動均衡了。
跟milvus社區維護人員咨詢過該問題,他們認為理論上擴增是會自動均衡的。這與我們測出的結果不匹配,后續會繼續跟進,找到問題所在。
持續大規模寫了很久后,會導致大量growing segment,導致查詢性能下降(跟進中)。
- 現象描述:
多個線程,持續大規模插入向量數據后,通過日志排查,發現部分部分查詢節點上的segment一直處于growing狀態,雖然這些segment在寫入節點已經sealed了,但是某個查詢節點并不會自動重新加載這些sealed segments,而是一直認為這些節點處于growing狀態。
由于growing 狀態的segment查詢時不用索引,而是暴力搜索,這樣會導致查詢變的比較慢,需要手動操作release。
- 當前進度:
跟milvus社區維護人員咨詢過該問題,后續還要持續跟進,找出原因并改進。
版本升級后,原有數據不兼容(已有方案)。
版本升級后,原有數據不兼容(已有方案)。
- 現象描述:
milvus版本由2.1.4升級到最新版后,原有數據沒辦法加載,且啟動不了。回退版本后,發現數據元信息已經被寫壞了,沒法加載。 - 解決方案:
后續穩定后,謹慎做版本升級,或升級前做好充分調研。另外官方給出的建議是升級前先merge數據。
千萬級別數據,壓測QPS不能達到預期(跟進中)
- 現象描述:
當數據插入千萬級別后,發現壓測提升QPS比較難,99%耗時下降也比較快,即使通過提升cpu核的個數,提升也不是很明顯。
比如以下是使用兩個 32核 16G:
- 解決方案:
這個可能跟我們使用FLAT索引有關,后續會嘗試新的索引方式壓測。
不要通過deployment擴容縮容,盡量通過helm去操作
- 現象描述:
當通過deployment擴容后,因為參數不能統一修改的問題,做不到平滑擴容,比如擴容后可能需要重新release與load數據,造成短時間中斷。
所以官網也給出建議盡量通過helm去平滑擴容。
6、總結
經過壓測,milvus是可以滿足我們當前業務場景的。以上壓測中的一些遺留問題,我們還在跟進中,比如:大量growing segment問題,節點擴增等問題。這些問題并不是100%出現的,有些是在我們極端測試條件下才出現的。后續我們還會持續測試,定位原因,并反饋給社區進一步優化。以上壓測的索引采用的是FLAT,官方建議我們采用圖索引可以取得更高性能。由于我們當前的業務場景要用到FLAT索引,所以當前先基于FLAT索引去壓測。后續會用到圖索引,也會進行壓測。
通過對milvus的壓測,順便了解并學習下milvus的設計。總體來說milvus是一款優秀的云原生向量數據庫,它的一些設計理念還是比較先進的,把向量搜索與k8s結合在一起,通過簡單的查詢節點擴增便可以線性提升向量搜索的性能。對于一款分布式數據庫,它實現了讀寫分離,存算分離。官網給出的文檔也比較豐富,工具也比較多,比如:attu,birdwatcher等。