向量資料庫如何處理資料?
封面圖片
在本博客系列的前兩篇文章中,我們已經介紹了世界上最先進的向量資料庫 Milvus 的系統架構,以及它的Python SDK 和 API。
這篇文章主要是希望透過深入了解 Milvus 系統,以及檢視資料處理元件之間的互動關係,幫助您瞭解 Milvus 是如何處理資料的。
在開始之前,下面列出了一些有用的資源。我們建議您先閱讀這些資源,以便更好地理解本文章的主題。
MsgStream 介面
MsgStream 介面對 Milvus 的資料處理非常重要。當Start()
被呼叫時,後台的 coroutine 將資料寫入log broker或從那裡讀取資料。當Close()
被呼叫時,coroutine 停止。
MsgStream 介面
MsgStream 可以作為生產者和消費者。AsProducer(channels []string)
介面將 MsgStream 定義為生產者,而AsConsumer(channels []string, subNamestring)
則將其定義為消費者。channels
這個參數在兩個介面中都是共用的,用來定義要寫入資料或讀取資料的 (實體) 通道。
集合中的分片數量可以在建立集合時指定。每個分片對應一個虛擬通道 (vchannel)。因此,一個集合可以有多個 vchannel。Milvus 會為記錄中介中的每個 v 通道指定一個實體通道 (pchannel)。
每個虛擬通道/shard 對應一個實體通道。
Produce()
在 MsgStream 介面中,負責將資料寫入 log broker 中的 pchannel。資料可以兩種方式寫入:
- 單一寫入:實體依據主鍵的散列值寫入不同的分片 (vchannel)。然後,這些實體會流入日誌經紀人中對應的 pnchanels。
- 廣播寫入:實體會寫入參數
channels
指定的所有 pchannels。
Consume()
是一種封鎖式 API。如果指定的 pchannel 中沒有可用的資料,則當在 MsgStream 介面中呼叫 時,coroutine 將被阻塞。另一方面, 是一種非阻塞式 API,這表示只有在指定的 pchannel 中存在現有資料時,coroutine 才會讀取和處理資料。否則,coroutine 可以處理其他任務,不會在沒有可用資料時阻塞。Consume()
Chan()
Seek()
是故障恢復的方法。當一個新的節點啟動時,可以取得資料消耗記錄,並透過呼叫 從中斷的地方恢復資料消耗。Seek()
寫入資料
寫入不同 v 通道 (shards) 的資料可以是插入訊息或刪除訊息。這些 vchannels 也可以稱為 DmChannels(資料操作通道)。
不同的集合可以在日誌中介中共用相同的 pchannels。一個集合可以有多個分片,因此也可以有多個對應的 v 通道。因此,同一集合中的實體會流入日誌中介中多個對應的 pchannels。因此,共用 pchannels 的好處是透過日誌經紀人的高並發能力來增加吞吐量。
當建立一個集合時,不僅要指定分片的數量,還要決定日誌經紀人中 vchannels 和 pchannels 之間的映射。
Milvus 中的寫入路徑
如上圖所示,在寫入路徑中,代理透過 MsgStream 的AsProducer()
介面將資料寫入日誌經紀人。然後,資料節點消耗資料,再將消耗的資料轉換並儲存到物件儲存中。儲存路徑是一種元資訊,會由資料協調器記錄在 etcd 中。
流程圖
由於不同的集合可能會共用日誌經紀人中相同的 pnchanels,因此在消耗資料時,資料節點或查詢節點需要判斷 pchannel 中的資料屬於哪個集合。為了解決這個問題,我們在 Milvus 中引入了 flowgraph。它主要負責根據集合 ID 過濾共用 pchannel 中的資料。因此,我們可以說每個 flowgraph 處理集合中相對應的 shard (vchannel) 的資料流。
寫入路徑中的流程圖
MsgStream 建立
寫入資料時,MsgStream 物件會在下列兩種情況下建立:
- 當代理程式接收到資料插入請求時,它會先嘗試透過根協調器 (root coordinator) 取得 vchannel 和 pchannels 之間的對應關係。然後,代理會建立一個 MsgStream 物件。
情況一
- 當資料節點啟動並讀取 etcd 中通道的 meta 資訊時,會建立 MsgStream 物件。
情況 2
讀取資料
在 Milvus 中讀取路徑
讀取資料的一般工作流程如上圖所示。查詢請求透過 DqRequestChannel 廣播到查詢節點。查詢節點並行執行查詢任務。來自查詢節點的查詢結果會透過 gRPC 和代理匯總結果,並將結果回傳給用戶端。
要仔細觀察資料讀取過程,我們可以看到代理將查詢請求寫入 DqRequestChannel。然後查詢節點透過訂閱 DqRequestChannel 來消耗訊息。DqRequestChannel 中的每條訊息都會進行廣播,讓所有訂閱的查詢節點都能收到訊息。
當查詢節點收到查詢請求時,它們會對儲存於密封區段的批次資料,以及動態插入 Milvus 並儲存於成長區段的串流資料進行本機查詢。之後,查詢節點需要聚合密封區段和成長區段中的查詢結果。這些聚合結果會透過 gRPC 傳送給代理。
代理收集來自多個查詢節點的所有結果,然後匯總這些結果以獲得最終結果。然後,代理將最終的查詢結果回傳給用戶端。由於每個查詢請求及其對應的查詢結果都有相同的唯一 requestID,因此代理可以找出哪個查詢結果對應於哪個查詢請求。
流程圖
讀取路徑中的流程圖
類似於寫入路徑,讀取路徑中也引入了 flowgraph。Milvus 實現了統一的 Lambda 架構,整合了增量資料和歷史資料的處理。因此,查詢節點也需要取得即時的串流資料。同樣地,讀取路徑中的 flowgraph 也會過濾和區分來自不同集合的資料。
建立 MsgStream
在讀取路徑中建立 MsgStream 物件
讀取資料時,MsgStream 物件會在以下情況建立:
- 在 Milvus 中,除非資料已被載入,否則無法讀取資料。當代理收到資料載入請求時,它會將請求傳送至查詢協調器,由查詢協調器決定將分片指派給不同查詢節點的方式。分配資訊(即 vchannels 的名稱以及 vchannels 與其對應的 pchannels 之間的映射)會透過方法呼叫或 RPC(遠端程序呼叫)傳送至查詢節點。隨後,查詢節點會建立相應的 MsgStream 物件來消耗資料。
DDL 作業
DDL 代表資料定義語言。元資料上的 DDL 作業可分為寫入請求與讀取請求。不過,在處理元資料時,這兩種請求會被同等對待。
元資料上的讀取請求包括
- 查詢集合模式
- 查詢索引資訊 等等
寫入請求包括
- 建立資料集
- 刪除集合
- 建立索引
- 丟棄索引 還有更多
DDL 請求從用戶端傳送至代理,代理進一步將這些請求以接收到的順序傳送給根協調器,根協調器為每個 DDL 請求指定一個時間戳,並對請求進行動態檢查。代理以序列方式處理每個請求,也就是一次處理一個 DDL 請求。代理在完成處理上一個請求並從根坐標收到結果之前,不會處理下一個請求。
DDL 操作。
如上圖所示,Root coord 任務佇列中有K
DDL 請求。任务队列中的 DDL 请求按照根协调器收到的顺序排列。因此,ddl1
是第一個傳送給根協調,而ddlK
是這一批中的最後一個。根協調器會按照時間順序逐一處理這些請求。
在分散式系統中,代理伺服器與根協調器之間的通訊是透過 gRPC 來啟用的。根協調器會保留已執行任務的最大時間戳值記錄,以確保所有 DDL 請求都會依時間順序處理。
假設有兩個獨立的代理,代理 1 和代理 2。它們都會傳送 DDL 請求到相同的根目錄。但是,有一個問題是,較早的請求不一定會在較後的另一個代理收到的請求之前傳送到根協定。例如,在上圖中,當DDL_K-1
從代理 1 傳送至根坐標時,代理 2 的DDL_K
已經被根坐標接受並執行。根據根坐標的記錄,此時已執行任務的最大時間戳值為K
。因此,為了不打亂時間順序,根目錄的任務佇列將拒絕DDL_K-1
的請求。但是,如果代理 2 在此點將請求DDL_K+5
傳送給根坐標,則該請求將被接受到任務佇列,並將在稍後根據其時間戳值執行。
建立索引
建立索引
當從用戶端接收到建立索引的請求時,代理會先對這些請求進行靜態檢查,然後將它們傳送給根協調。然後,根協調器會將這些索引建立請求持久化到元儲存 (etcd) 中,並將請求傳送給索引協調器 (索引協調器)。
建立索引。
如上圖所示,當索引協調器收到來自根協調器的索引建立請求時,它會先將任務持久化到 etcd 中的元儲存區。索引建立任務的初始狀態是Unissued
。索引協調會維護每個索引節點的任務負載記錄,並將進入的任務傳送到負載較少的索引節點。任務完成後,索引節點會將該任務的狀態Finished
或Failed
寫入 meta 儲存,也就是 Milvus 中的 etcd。之後,索引協調器就會透過在 etcd 中查詢,瞭解建立索引的任務是成功還是失敗。如果任務因為系統資源有限或索引節點丟失而失敗,索引協調器會重新觸發整個流程,並將相同的任務指派給另一個索引節點。
丟棄索引
此外,索引協調員也負責刪除索引的請求。
丟棄索引
當根協調器收到用戶端的刪除索引請求時,它會首先將索引標記為 "drop",並將結果返回給用戶端,同時通知索引協調器。然後,索引協調器使用IndexID
過濾所有索引任務,符合條件的任務會被丟棄。
索引協調器的背景 coroutine 會逐漸從物件儲存空間 (MinIO 和 S3) 刪除所有標示為 "dropped" 的索引任務。這個過程涉及 recycleIndexFiles 介面。當所有對應的索引檔案刪除後,被刪除索引任務的元資訊也會從元儲存 (etcd) 中移除。
關於 Deep Dive 系列
隨著 Milvus 2.0正式宣布全面上市,我們安排了這個 Milvus Deep Dive 系列部落格,提供對 Milvus 架構和原始碼的深入詮釋。本系列部落格涵蓋的主題包括
- MsgStream 介面
- 寫入資料
- 讀取資料
- DDL 作業
- 建立索引
- 關於 Deep Dive 系列
On This Page
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word