Milvus 如何實現數據動態更新與查詢

在這篇文章,咱們會主要描述 Milvus 裏向量數據是如何被記錄在內存中,以及這些記錄以怎樣的形式維護。git

咱們的設計目標主要有下面三點:github

  1. 數據導入效率要高
  2. 數據導入後儘快可見
  3. 避免數據文件碎片化

所以,咱們創建了插入數據的內存緩衝區(insert buffer),以減小磁盤隨機 IO 和操做系統中上下文切換的次數,從而提高數據插入的性能。基於 MemTable 和 MemTableFile 的內存存儲架構,能使咱們更加方便的管理和序列化數據。將 buffer 的狀態分爲 Mutable 和 Immutable,能讓數據持久化到磁盤的同時保持對外服務可用。架構

|準備

當用戶準備插入向量到 Milvus 時,首先須要建立一個 Collection(*Milvus 在0.7.0版本中將 Table 改名爲 Collection)。Collection 是 Milvus 記錄和搜索向量的最基本單位。每一個 Collection 有一個獨特的名字和一些能夠被設置的屬性,而且根據 Collection 的名字進行向量的插入或搜索。建立一個新的 Collection 時,Milvus 會在元數據裏記錄下這個 Collection 的信息。異步

|數據的插入

當用戶發出插入數據的請求時,數據通過序列化和反序列化,到達 Milvus server。數據這時候開始寫入內存。內存寫入大體分爲下面幾個步驟:分佈式

  1. 在 MemManager 中,找到或新建立與Collection 名字對應的 MemTable。每一個 MemTable對應一個 Collection 在內存中的 buffer。
  2. 一個 MemTable 會包含一個或多個 MemTableFile。每當咱們建立一個新的 MemTableFile,咱們會同時在 Meta 中記錄這個信息。咱們將 MemTableFile 分爲兩種狀態:Mutable 和 Immutable。當 MemTableFile 大小達到閾值,會變成 Immutable 狀態。每一個 Memtable 在任意時間只會存在一個 Mutable MemTableFile 可被寫入。
  3. 每一個 MemTableFile 的數據會最終以被設置的 index 類型的格式記錄在內存裏。MemTableFile 是在內存中管理數據的最基本單位。
  4. 任意時刻,插入數據的內存的佔用量都不會超過預先設置的值(insert_buffer_size)。這是由於每個插入數據的請求進來,MemManager 均可以很方便的計算到每一個 MemTable 下包含的 MemTableFile 所佔內存,而後根據當前內存協調插入請求。

經過 MemManager, MemTable 和 MemTableFile 多層級的架構,數據的插入能夠更好地被管理和維護。固然,它們能作的遠不止這些。性能

| 近實時查詢

在 Milvus 裏,從數據被記錄在內存,到數據能被搜到,你最快只須要等待一秒。這整個過程能夠大概由下面這張圖來歸納:spa

首先,插入的數據會進入一個內存中的 insert buffer。這些 buffer 會由開始的 Mutable 狀態週期性的轉爲 Immutable 狀態,以準備序列化。而後,這些 Immutable buffer 會週期性的被後臺序列化線程序列化到磁盤。數據落盤後,落盤信息會被記錄在元數據裏。至此,數據就能被搜到了!操作系統

如今,咱們會具體描述圖中的步驟。.net

數據插入 Mutable buffer 的過程咱們都已經知道了,接下來,就是從 Mutable buffer 轉爲 Immutable buffer 的過程:線程

Immutable queue 這個隊列會向後臺序列化線程提供 immutable 狀態的,已經準備好被序列化的 MemTableFile。每一個 MemTable 管理着本身的 immutable queue,當 MemTable 惟一 mutable 的 MemTableFile 大小達到閾值,就會進入 immutable queue。一個負責 ToImmutable 的後臺線程會週期性的拉取全部 MemTable 管理的 immutable queue 中的 MemTableFile,並將他們輸送到總的 Immutable queue。須要注意的是,數據寫入內存和將內存中的數據變爲不可被寫的狀態這兩個操做不能同時發生,須要共用一把鎖。可是,ToImmutable 這個操做由於過程很簡單,幾乎不會形成任何延遲,因此對插入數據的性能影響微乎其微。

接下來就是將 serialization queue 中的 MemTableFile 序列化到磁盤了。這主要分爲三步:

首先,後臺序列化線程會週期性的從 immutable queue 中拉取 MemTableFile。而後,他們被序列化成固定大小的原始文件(Raw TableFiles)。最後,咱們會將這個信息記錄在元數據中。當咱們進行向量搜索時,咱們會在元數據中查詢對應的 TableFile。至此爲止,這些數據就能被搜索到了!

此外,根據設置的 index_file_size,後臺序列化線程在完成一次序列化週期後,會將一些固定大小的 TableFile 合併成一個 TableFile,而且一樣在元數據中記錄這些信息。這時候,這個 TableFile 就能夠被構建索引了。構建索引一樣也是異步的,另一個負責構建索引的後臺線程會週期性的讀取元數據中 ToIndex 狀態的 TableFile,進行對應的索引構建。

| 向量搜索

實際上,你會發現,經過 TableFile 和元數據的幫助,向量的搜索變得更加直觀和方便。大致上說,咱們須要從元數據中獲取與被查詢 Collection 對應的 TableFiles,在每一個 TableFile 進行搜索,最後進行歸併。在這篇文章裏,咱們不深刻探討搜索的具體實現。若是你想要了解更多,歡迎閱讀咱們的源碼,或者閱讀 Milvus 系列的其餘文章!

更多Milvus 原理與最佳實踐系列文章

Milvus 分佈式向量檢索-Mishards架構介紹

Milvus 查詢任務調度原理

Milvus 最佳實踐之如何設置API參數 (3)

Milvus 最佳實踐之如何設置系統配置項 (2)

Milvus 最佳實踐之如何選擇索引類型

歡迎加入 Milvus 社區

http://github.com/milvus-io/milvus| 源碼

http://milvus.io| 官網

http://milvusio.slack.com| Slack 社區

http://zilliz.blog.csdn.net| CSDN 博客

相關文章
相關標籤/搜索