🚀 免費嘗試 Zilliz Cloud,完全托管的 Milvus,體驗速度提升 10 倍!立即嘗試

milvus-logo
LFAI

使用方法

  • Engineering
February 07, 2022
Lichen Wang

Milvus 2.0 擁有統一的批次和流處理以及雲原生架構,在 DELETE 功能的開發過程中,Milvus 2.0 比其前身面臨更大的挑戰。得益於先進的儲存-計算分解設計和靈活的發布/訂閱機制,我們很自豪地宣佈我們實現了這一目標。在 Milvus 2.0 中,您可以使用主鍵刪除指定集合中的實體,這樣被刪除的實體就不會再被列在搜尋或查詢的結果中。

請注意,Milvus 中的 DELETE 操作指的是邏輯刪除,而實體資料清理發生在資料壓縮期間。邏輯刪除不僅大大提升了受限於 I/O 速度的搜尋效能,也方便了資料復原。邏輯刪除的資料仍可在時間旅行功能的協助下復原。

使用方法

讓我們先試試 Milvus 2.0 中的 DELETE 函式。(以下範例在 Milvus 2.0.0 上使用 PyMilvus 2.0.0)。

from pymilvus import connections, utility, Collection, DataType, FieldSchema, CollectionSchema
# Connect to Milvus
connections.connect(
    alias="default", 
    host='x.x.x.x', 
    port='19530'
)
# Create a collection with Strong Consistency level
pk_field = FieldSchema(
    name="id", 
    dtype=DataType.INT64, 
    is_primary=True, 
)
vector_field = FieldSchema(
    name="vector", 
    dtype=DataType.FLOAT_VECTOR, 
    dim=2
)
schema = CollectionSchema(
    fields=[pk_field, vector_field], 
    description="Test delete"
)
collection_name = "test_delete"
collection = Collection(
    name=collection_name, 
    schema=schema, 
    using='default', 
    shards_num=2,
    consistency_level="Strong"
)
# Insert randomly generated vectors
import random
data = [
    [i for i in range(100)],
    [[random.random() for _ in range(2)] for _ in range(100)],
]
collection.insert(data)
# Query to make sure the entities to delete exist
collection.load()
expr = "id in [2,4,6,8,10]"
pre_del_res = collection.query(
    expr,
    output_fields = ["id", "vector"]
)
print(pre_del_res)
# Delete the entities with the previous expression
collection.delete(expr)
# Query again to check if the deleted entities exist
post_del_res = collection.query(
    expr,
    output_fields = ["id", "vector"]
)
print(post_del_res)

執行

在 Milvus 實例中,資料節點主要負責將串流資料 (log broker 中的日誌) 封裝成歷史資料 (日誌快照),並自動將它們沖洗到物件儲存空間。查詢節點在完整資料(即串流資料和歷史資料)上執行搜尋請求。

為了充分利用集群中平行節點的資料寫入能力,Milvus 採用基於主索引鍵雜湊的分片策略,將寫入作業平均分配到不同的工作節點。也就是說,proxy 會將實體的資料處理語言 (Data Manipulation Language, DML) 訊息 (即請求) 路由到相同的資料節點和查詢節點。這些訊息透過 DML 通道發佈,並由資料節點和查詢節點分別消耗,以共同提供搜尋和查詢服務。

資料節點

接收到資料 INSERT 訊息後,資料節點會將資料插入一個成長中的區段,這個區段是為了接收記憶體中的串流資料而建立的新區段。如果資料行數或成長區段的持續時間達到臨界值,資料節點就會封鎖它,以防止任何資料進入。然後,資料節點會將封閉區段 (包含歷史資料) 沖洗至物件儲存區。同時,資料節點根據新資料的主鍵產生 Bloom filter,並將其與封存的區段一起沖洗到物件儲存區,將 Bloom filter 儲存為統計二進位日誌 (binlog) 的一部分,其中包含區段的統計資訊。

bloom filter 是一種概率資料結構,由一個長的二進位向量和一系列隨機映射函數所組成。它可以用來測試某個元素是否為某個集合的成員,但可能會回傳假陽性的匹配結果。 -- 維基百科

當資料 DELETE 訊息傳入時,資料節點會緩衝相應分片中的所有 Bloom 過濾器,並將它們與訊息中提供的主索引鍵比對,以擷取可能包含要刪除實體的所有區段 (從成長中和封存中)。找出對應的區段後,資料節點會將區段緩衝到記憶體中,以產生 Delta binlog 來記錄刪除作業,然後將這些 binlog 連同區段一起沖回物件儲存空間。

Data Node 資料節點

由於一個分片只指定一個 DML-Channel,額外加入群集的查詢節點無法訂閱 DML-Channel。為了確保所有查詢節點都能收到 DELETE 訊息,資料節點會過濾 DML-Channel 的 DELETE 訊息,並將訊息轉送至 Delta-Channel,以通知所有查詢節點刪除作業。

查詢節點

從物件儲存空間載入集合時,查詢節點會先取得每個分片的檢查點 (checkpoint),該檢查點會標記自上次刷新 (flush) 作業後的 DML 作業。根據檢查點,查詢節點會載入所有封存區段及其 Delta binlog 和 Bloom 過濾器。所有資料載入後,查詢節點會訂閱 DML-Channel、Delta-Channel 和 Query-Channel。

如果在資料集載入到記憶體之後,有更多的資料 INSERT 訊息傳來,查詢節點會先根據這些訊息找出成長中的區段,並更新記憶體中對應的 Bloom 過濾器,僅供查詢之用。這些查詢專用的 Bloom 過濾器在查詢完成後,不會被刷新到物件儲存空間。

Query Node 查詢節點

如上所述,只有一定數量的查詢節點可以從 DML-Channel 接收 DELETE 訊息,也就是只有它們可以執行成長中的 DELETE 請求。對於那些訂閱了 DML 通道的查詢節點,它們會先過濾成長區段中的 DELETE 訊息,透過所提供的主索引鍵與成長區段中的查詢專用 Bloom 過濾器進行比對來找出實體,然後在對應的區段中記錄刪除作業。

不能訂閱 DML-Channel 的查詢節點,因為只能訂閱 Delta-Channel,所以只能處理封存區段上的搜尋或查詢請求,並接收資料節點轉寄的 DELETE 訊息。查詢節點從 Delta-Channel 收集到封存區段中的所有 DELETE 訊息後,透過提供的主鍵與封存區段的 Bloom 過濾器進行比對,找出實體,然後在對應的區段中記錄刪除作業。

最後,在搜尋或查詢中,查詢節點會根據刪除記錄產生一個位元集,以省略刪除的實體,並在所有區段中的剩餘實體中進行搜尋,而不受區段狀態的限制。最後,一致性等級會影響刪除資料的可見性。在強一致性層級下(如之前的程式碼範例所示),刪除的實體在刪除後立即不見。如果採用 Bounded Consistency Level,則會有幾秒鐘的延遲,刪除的實體才會變成不可見。

下一步是什麼?

在 2.0 新功能系列部落格中,我們的目標是解釋新功能的設計。閱讀此系列部落格的更多內容!

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started

Like the article? Spread the word

繼續閱讀