🚀 Testen Sie Zilliz Cloud, die vollständig verwaltete Milvus, kostenlos – erleben Sie 10x schnellere Leistung! Jetzt testen>>

milvus-logo
LFAI

Verwendung

  • Engineering
February 07, 2022
Lichen Wang

Mit der vereinheitlichten Batch- und Stream-Verarbeitung und der Cloud-nativen Architektur stellt Milvus 2.0 eine größere Herausforderung dar als sein Vorgänger bei der Entwicklung der DELETE-Funktion. Dank des fortschrittlichen Disaggregationsdesigns für die Speicherberechnung und des flexiblen Veröffentlichungs-/Abonnement-Mechanismus können wir mit Stolz verkünden, dass wir es geschafft haben. In Milvus 2.0 können Sie eine Entität in einer bestimmten Sammlung mit ihrem Primärschlüssel löschen, so dass die gelöschte Entität nicht mehr im Ergebnis einer Suche oder einer Abfrage aufgeführt wird.

Bitte beachten Sie, dass sich die DELETE-Operation in Milvus auf die logische Löschung bezieht, während die physische Datenbereinigung während der Datenverdichtung stattfindet. Logisches Löschen erhöht nicht nur die durch die E/A-Geschwindigkeit eingeschränkte Suchleistung, sondern erleichtert auch die Datenwiederherstellung. Logisch gelöschte Daten können mit Hilfe der Zeitreisefunktion wiederhergestellt werden.

Verwendung

Lassen Sie uns zunächst die DELETE-Funktion in Milvus 2.0 ausprobieren. (Das folgende Beispiel verwendet PyMilvus 2.0.0 auf Milvus 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)

Implementierung

In einer Milvus-Instanz ist ein Datenknoten hauptsächlich dafür verantwortlich, Streaming-Daten (Logs im Log-Broker) als historische Daten (Log-Snapshots) zu packen und automatisch in den Objektspeicher zu flushen. Ein Abfrageknoten führt Suchanfragen auf vollständigen Daten aus, d. h. sowohl auf Streaming-Daten als auch auf historischen Daten.

Um die Datenschreibkapazitäten der parallelen Knoten in einem Cluster optimal zu nutzen, verwendet Milvus eine Sharding-Strategie, die auf Primärschlüssel-Hashing basiert, um die Schreibvorgänge gleichmäßig auf verschiedene Arbeitsknoten zu verteilen. Das heißt, dass der Proxy die DML-Nachrichten (d.h. Anfragen) einer Entität an denselben Daten- und Abfrageknoten weiterleitet. Diese Nachrichten werden über den DML-Kanal veröffentlicht und von dem Datenknoten und dem Abfrageknoten getrennt konsumiert, um Such- und Abfragedienste gemeinsam bereitzustellen.

Datenknoten

Nach dem Empfang von INSERT-Nachrichten fügt der Datenknoten die Daten in ein wachsendes Segment ein, d. h. in ein neues Segment, das für den Empfang von Streaming-Daten im Speicher erstellt wird. Wenn entweder die Anzahl der Datenzeilen oder die Dauer des wachsenden Segments den Schwellenwert erreicht, versiegelt der Datenknoten es, um weitere eingehende Daten zu verhindern. Der Datenknoten spült dann das versiegelte Segment, das die historischen Daten enthält, in den Objektspeicher. In der Zwischenzeit generiert der Datenknoten einen Bloomfilter auf der Grundlage der Primärschlüssel der neuen Daten und spült ihn zusammen mit dem versiegelten Segment in den Objektspeicher, wobei er den Bloomfilter als Teil des binären Statistikprotokolls (binlog) speichert, das die statistischen Informationen des Segments enthält.

Ein Bloomfilter ist eine probabilistische Datenstruktur, die aus einem langen binären Vektor und einer Reihe von Zufallsabbildungsfunktionen besteht. Er kann verwendet werden, um zu testen, ob ein Element Mitglied einer Menge ist, kann aber auch falsch positive Treffer liefern. -- Wikipedia

Wenn Daten-DELETE-Nachrichten eingehen, puffert der Datenknoten alle Bloom-Filter im entsprechenden Shard und gleicht sie mit den in den Nachrichten angegebenen Primärschlüsseln ab, um alle Segmente (sowohl die wachsenden als auch die versiegelten) abzurufen, die möglicherweise die zu löschenden Entitäten enthalten. Nachdem die entsprechenden Segmente lokalisiert wurden, puffert der Datenknoten sie im Speicher, um die Delta-Binlogs zur Aufzeichnung der Löschvorgänge zu erzeugen, und spült diese Binlogs dann zusammen mit den Segmenten zurück in den Objektspeicher.

Data Node Datenknoten

Da einem Shard nur ein DML-Kanal zugewiesen ist, können zusätzliche Abfrageknoten, die dem Cluster hinzugefügt werden, den DML-Kanal nicht abonnieren. Um sicherzustellen, dass alle Abfrageknoten die DELETE-Nachrichten empfangen können, filtern die Datenknoten die DELETE-Nachrichten aus dem DML-Kanal und leiten sie an den Delta-Kanal weiter, um alle Abfrageknoten über die Löschvorgänge zu informieren.

Abfrageknoten

Beim Laden einer Sammlung aus dem Objektspeicher holt sich der Abfrageknoten zunächst den Prüfpunkt jedes Shards, der die DML-Vorgänge seit dem letzten Flush-Vorgang markiert. Auf der Grundlage des Prüfpunkts lädt der Abfrageknoten alle versiegelten Segmente zusammen mit ihren Delta-Binlog- und Bloom-Filtern. Wenn alle Daten geladen sind, abonniert der Abfrageknoten DML-Channel, Delta-Channel und Query-Channel.

Wenn weitere INSERT-Nachrichten eintreffen, nachdem die Sammlung in den Speicher geladen wurde, bestimmt der Abfrageknoten zunächst die wachsenden Segmente entsprechend den Nachrichten und aktualisiert die entsprechenden Bloomfilter im Speicher nur für Abfragezwecke. Diese für die Abfrage bestimmten Bloomfilter werden nach Abschluss der Abfrage nicht in den Objektspeicher gespült.

Query Node Abfrageknoten

Wie oben erwähnt, kann nur eine bestimmte Anzahl von Abfrageknoten DELETE-Nachrichten vom DML-Kanal empfangen, d.h. nur sie können die DELETE-Anfragen in wachsenden Segmenten ausführen. Diejenigen Abfrageknoten, die den DML-Kanal abonniert haben, filtern zunächst die DELETE-Nachrichten in den wachsenden Segmenten, suchen die Entitäten, indem sie die bereitgestellten Primärschlüssel mit den abfragespezifischen Bloomfiltern der wachsenden Segmente abgleichen, und zeichnen dann die Löschvorgänge in den entsprechenden Segmenten auf.

Abfrageknoten, die den DML-Kanal nicht abonnieren können, dürfen nur Such- oder Abfrageanfragen auf versiegelten Segmenten verarbeiten, da sie nur den Delta-Kanal abonnieren und die von Datenknoten weitergeleiteten DELETE-Nachrichten empfangen können. Nachdem die Abfrageknoten alle DELETE-Nachrichten in den versiegelten Segmenten des Delta-Kanals gesammelt haben, lokalisieren sie die Entitäten, indem sie die angegebenen Primärschlüssel mit den Bloom-Filtern der versiegelten Segmente abgleichen, und zeichnen dann die Löschvorgänge in den entsprechenden Segmenten auf.

Schließlich generieren die Abfrageknoten bei einer Suche oder Abfrage ein Bitset auf der Grundlage der Löschaufzeichnungen, um die gelöschten Entitäten auszulassen, und suchen unter den verbleibenden Entitäten aus allen Segmenten, unabhängig vom Segmentstatus. Nicht zuletzt beeinflusst die Konsistenzstufe die Sichtbarkeit der gelöschten Daten. Unter Strong Consistency Level (wie im vorherigen Codebeispiel gezeigt) sind die gelöschten Entitäten sofort nach dem Löschen unsichtbar. Bei Bounded Consistency Level dauert es mehrere Sekunden, bis die gelöschten Objekte unsichtbar werden.

Was kommt als Nächstes?

In der Blogserie zu den neuen Funktionen 2.0 wollen wir das Design der neuen Funktionen erklären. Lesen Sie mehr in dieser Blogserie!

Try Managed Milvus for Free

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

Get Started

Like the article? Spread the word

Weiterlesen