milvus安裝及其使用教程


重要提示

本博客距今已有半年時間,期間milvus有了很多更新,好多結論和范例代碼已經不再適用,比如milvus已經支持ndarray格式的插入。本人重新寫了一篇,同時加入了faiss向量搜索工具的安裝和使用,
由於本人在博客園有了新的賬號,當前賬號將會被注銷,故不再更新此文,請移步此處milvus和faiss安裝及其使用教程查看最新文章。

milvus 簡介

milvus是干什么的?通俗的講,milvus可以讓你在海量向量庫中快速檢索到和目標向量最相似的若干個向量,這里相似度量標准可以是內積或者歐式距離等。借用官方的話說就是:

Milvus 是一款開源的、針對海量特征向量的相似性搜索引擎。基於異構眾核計算框架設計,成本更低,性能更好。 在有限的計算資源下,十億向量搜索僅毫秒響應。

說白了就是速度快,先不說十億向量,自己寫代碼去完成對100萬300維向量的余弦相似度計算並排序就需要不小的響應時間吧,就本人測試而言,即便使用scipy庫計算速度依然要比milvus慢很多。

milvus和Faiss的對比

其實在milvus開源之前,也存在高性能向量相似性搜索引擎(庫),這個引擎就是Facebook的Faiss,它的功能和milvus是一樣的,所以就不再做過多介紹,具體可以參考官網

就我個人而言,我是推薦使用milvus的,主要是在我個人看來,milvus有如下幾個好處:

  • 多平台通用,mac,windows和linux都是支持的,因為milvus可以通過docker部署,因此平台通用性好了不少。
  • 支持編程語言多,Java,c,c++和python都支持,要知道Faiss是不支持java的,這一點簡直讓人抓狂,github上好幾個項目就是關於把Faiss轉成java的,因為我Java和python都是要使用的,我把github上關於faiss轉java的項目都試了個遍,結論就是非常難安裝,只要Faiss版本更新了,必須要重來一遍,即便最后java可以用了,也不敢保證其穩定性。所以想在Java上用Faiss還是放棄吧。
  • 在速度方面,就我自己測試而言,milvus不輸Faiss,但是我沒有使用GPU測試,有興趣的小伙伴可以試一下。

當然milvus也有難用的地方,我自己發現了兩點,如果是我自己使用不當造成的,還請各位朋友在評論指出:

  • milvus的向量格式不支持numpy,要用列表的形式存儲向量,而列表占用內存要遠遠大於ndarray的,這對於小內存主機簡直是個天災,我16GB內存情況下,把500000*768的矩陣轉成list,電腦直接卡死。
  • milvus在第一次search時速度會慢,之后機會快起來了,這應該是個小bug,有人在github提出了,應該很快就可以修復,所以你們在測試milvus的速度時千萬別算第一次的時間。

milvus 安裝及常見問題

milvus 一共有兩種安裝方式:自己編譯安裝和使用docker安裝。這里推薦大家使用docker安裝,docker安裝方便快捷,可在Windows上使用。自己編譯安裝,由於每個人環境不同,很容易出問題。本文只介紹基於docker的安裝,另外因為我比較窮,所以只介紹cpu版本的安裝,不過gpu安裝也是大同小異。

基於docker安裝milvus CPU版本

簡言之安裝比較簡單,畢竟大佬們已經把milvus部署在了docker里,我們只要啟動起來就行了

安裝docker

首先就是要安裝docker,還不了解docker可以了解一波,非常好用的虛擬機工具,直接去官網下載對應平台的安裝文件即可。

下載相應版本鏡像

安裝好docker后,要去pull對應的鏡像(image),首先進到dockerhub官網中,然后搜索milvus,第一個結果就是。因為我們安裝的是CPU版本,所以在tags里找cpu-latest,然后pull下來就可以了,即在你的命令行窗口輸入
docker pull milvusdb/milvus:cpu-latest注意:隨着版本迭代更新,這一條命令在未來可能會失效,建議先去dockerhub搜索一下,去看一下應該用什么tag。

設置工作目錄和配置文件

在創建啟動容器之前,我們要先設置好工作目錄和配置文件。

一共要設置三個目錄,分別是數據庫存儲目錄,日志存儲目錄和配置文件目錄。其中配置文件目錄就存放着我說的配置文件。配置文件一共有兩個,分別是服務器設置文件和日志設置文件。

所以我們要想好這三個文件夾放在哪里,比如我們可以在當前用戶目錄下建立一個milvus文件夾,然后在這里面存儲上述三個目錄。下面我們需要設置兩個配置文件,記得要把服務器配置文件名改為server_config.yaml,把日志配置文件改為log_config.conf
兩個配置文件的內容:服務器配置文件 日志配置文件。配置文件也可以到官網下載

下面是我的文件目錄結構,共大家參考:

  milvus
    │
    ├─conf //配置文件目錄
    │      log_config.conf  //服務器配置文件
    │      server_config.yaml  //日志配置文件
    │
    ├─db //數據庫存儲目錄
    │
    └─logs //日志存儲目錄
    │

啟動docker服務

設置好工作目錄后,就可以使用鏡像創建容器了,我的工作目錄是C:\Users\Zhdun\milvus,所以我的創建命令是:

docker run -td --name mymilvus -e "TZ=Asia/Shanghai" -p 19530:19530 -p 8080:8080 -v C:\Users\Zhdun\milvus\db:/var/lib/milvus/db -v C:\Users\Zhdun\milvus\conf:/var/lib/milvus/conf -v C:\Users\Zhdun\milvus\logs:/var/lib/milvus/logs milvusdb/milvus:cpu-latest

命令看起來有點長, 我稍微解釋下,-td是后台運行,--name是給自己的容器起個名字,-p是端口映射,不想用默認的話,可以去服務器配置文件里改,-v就是為了映射三個工作目錄。具體可以參考docker的run命令

執行完命令后,運行docker ps -a,如果發現自己創建的容器excited的了,那就docker logs一下,看出了什么問題。如果發現容器在運行了,就代表基本沒問題了。

接下來我會說一下常見的安裝問題,以及如何去使用milvus。

安裝時的常見問題及解決

Config check fail: Invalid config version: . Expected config version: 0.1 遇到這種問題就在服務器的配置文件第一行加上version: 0.1

Config check fail: Invalid cpu cache capacity: 1. Possible reason: sum of cache_config.cpu_cache_capacity and db_config.insert_buffer_size exceeds system memory.
這種問題就說明內存超出了限制,首先檢查服務器配置里的 cpu_cache_capacity 和 insert_buffer_size 是不是過大了。

然后再檢查給定docker設定的內存是多少,可以通過docker info來檢查。

milvus 基本使用

安裝完成后,終於可以開始使用milvus了,milvus支持python,java和c++。在這里我只介紹python的使用。
首先安裝 pymilvus庫:pip install pymilvus,然后就可以使用這個庫來寫代碼了,接下來我會直接把自己寫的范例代碼貼上去,其中每一步的具體含義以及可能的擴展我會直接在注釋里告訴大家,如有錯誤還請各位指出。

# -*- coding: utf-8 -*-

#導入相應的包
import numpy as np
from milvus import Milvus, IndexType, MetricType

# 初始化一個Milvus類,以后所有的操作都是通過milvus來的
milvus = Milvus()

# 連接到服務器,注意端口映射,要和啟動docker時設置的端口一致
milvus.connect(host='localhost', port='19530')

# 向量個數
num_vec = 5000
# 向量維度
vec_dim = 768

# 創建表
# 參數含義
# table_name: 表名
# dimension: 向量維度
# metric_type: 向量相似度度量標准, MetricType.IP是向量內積; MetricType.L2是歐式距離
table_param = {'table_name': 'mytable', 'dimension':vec_dim, 'index_file_size':1024, 'metric_type':MetricType.IP}
milvus.create_table(table_param)

# 隨機生成一批向量數據
vectors_array = np.random.rand(num_vec,vec_dim)
vectors_list = vectors_array.tolist()

# 官方建議在插入向量之前,建議先使用 milvus.create_index 以便系統自動增量創建索引
# 索引類型有:FLAT / IVFLAT / IVF_SQ8 / IVF_SQ8H,其中FLAT是精確索引,速度慢,但是有100%的召回率
index_param = {'index_type': IndexType.FLAT, 'nlist': 128}
milvus.create_index('mytable', index_param)

# 把向量添加到剛才建立的表格中
# ids可以為None,使用自動生成的id
status, ids = milvus.add_vectors(table_name="mytable",records=vectors_list,ids=None) # 返回這一組向量的ID

# 官方建議 向量插入結束后,相同的索引需要手動再創建一次
milvus.create_index('mytable', index_param)

# 輸出一些統計信息
status, tables = milvus.show_tables()
print("所有的表格:",tables)
print("表格的數據量(行):{}".format((milvus.count_table('mytable')[1])))
print("mytable表格是否存在:",milvus.has_table("mytable")[1])

# 加載表格到內存
milvus.preload_table('mytable')

# 創建查詢向量
query_vec_array = np.random.rand(1,vec_dim)
query_vec_list = query_vec_array.tolist()
# 進行查詢, 注意這里的參數nprobe和建立索引時的參數nlist 會因為索引類型不同而影響到查詢性能和查詢准確率
# 對於 FLAT類型索引,兩個參數對結果和速度沒有影響
status, results = milvus.search(table_name='mytable', query_records=query_vec_list, top_k=4, nprobe=16)
print(status)
print(results)



# 刪除表格和索引, 不刪除的話,下一次還可以繼續使用
milvus.drop_index(table_name="mytable")
milvus.delete_table(table_name="mytable")

# 斷開連接
milvus.disconnect()

milvus 多進程使用

寫這一章的主要目的是為了進行並發測試,以及多進程能否節省時間,官方說明在使用多進程時需要滿足下面兩個條件:

  • 程序執行時主進程中沒有創建 client
  • 每個子進程分別創建 client 進行操作

下面是我的測試代碼:

# -*- coding: utf-8 -*-
import time
from multiprocessing import Pool
import numpy as np
import random
from milvus import Milvus, IndexType, MetricType

def create_data(host,port,num_vec,vec_dim):
    """ 創建一些表格和索引用來做多進程測試 """
    milvus = Milvus()
    milvus.connect(host=host, port=port)
    # 創建2個表
    table_param = {'table_name': 'table1', 'dimension':vec_dim, 'index_file_size':1024, 'metric_type':MetricType.IP}
    milvus.create_table(table_param)
    table_param = {'table_name': 'table2', 'dimension':vec_dim, 'index_file_size':1024, 'metric_type':MetricType.L2}
    milvus.create_table(table_param)
    # 隨機生成一批向量數據
    vectors_array = np.random.rand(num_vec,vec_dim)
    vectors_list = vectors_array.tolist()
    # 創建索引
    index_param = {'index_type': IndexType.FLAT, 'nlist': 128}
    milvus.create_index('table1', index_param)
    milvus.create_index('table2', index_param)

    # 添加數據
    milvus.add_vectors(table_name="table1",records=vectors_list,ids=None)
    milvus.add_vectors(table_name="table2",records=vectors_list,ids=None)

    # 創建索引
    milvus.create_index('table1', index_param)
    milvus.create_index('table2', index_param)
    print(milvus.show_tables())
    # 斷開連接
    milvus.disconnect()
def clear_table(host,port):
    """ 清空表格和索引 """
    milvus = Milvus()
    milvus.connect(host=host, port=port)
    for table_name in milvus.show_tables()[1]:
        milvus.drop_index(table_name=table_name)
        milvus.delete_table(table_name=table_name)
    milvus.disconnect()
def milvus_search(host,port,table_name,query_vec,search_time=10):
    """ 測試查詢, 返回查詢的秒數"""
    milvus = Milvus()
    milvus.connect(host=host, port=port)
    # 因為bug的原因,要先搜索一次
    milvus.search(table_name,4,8,query_vec)
    # 開始測試
    for _ in range(search_time):
        query_vec[0][0] = random.random() # 稍微隨機化一下
        milvus.search(table_name, 4, 8, query_vec)


if __name__  == "__main__":
    host = "localhost"
    port = "19530"
    num_vec = 100000
    vec_dim = 768
    num_proc = 3 # 進程數
    search_time = 2000 # 搜索次數
    ####### Step1 先創建用於測試的數據 運行一次就行了
    # create_data(host=host,port=port,num_vec=num_vec,vec_dim=vec_dim)
    # clear_table(host,port)
    # exit(0)

    ####### Step2 測試依次執行的時間
    start_time = time.time()
    for _ in range(num_proc):
        query_vec = np.random.rand(1,vec_dim).tolist()
        milvus_search(host,port,"table1",query_vec,search_time)
    end_time = time.time()
    print("順序執行milvus_search的時間總和是:",end_time-start_time)

    ####### Step3 測試多進程時間
    pool = Pool(num_proc)
    start_time = time.time()
    for _ in range(num_proc):
        query_vec = np.random.rand(1,vec_dim).tolist()
        pool.apply_async(milvus_search,args=(host,port,"table1",query_vec,search_time))
    pool.close()
    pool.join()
    end_time = time.time()
    print("並行執行milvus_search的時間總和是:",end_time-start_time)

結論就是對於search操作,依次search100次,和10個進程同時開,每個進程search10次,開多進程速度是會變快的。

感謝閱讀,如有問題,煩請指出。

文章可以隨意轉載,但請務必注明出處:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM