Come vengono elaborati i dati in un database vettoriale?
Immagine di copertina
Questo articolo è stato scritto da Zhenshan Cao e trascritto da Angela Ni.
Nei due post precedenti di questa serie di blog, abbiamo già trattato l'architettura del sistema di Milvus, il database vettoriale più avanzato al mondo, e il suo SDK e API Python.
Questo post si propone principalmente di aiutarvi a capire come vengono elaborati i dati in Milvus, approfondendo il sistema Milvus ed esaminando l'interazione tra i componenti di elaborazione dei dati.
Di seguito sono elencate alcune risorse utili prima di iniziare. Si consiglia di leggerle prima per comprendere meglio l'argomento di questo post.
- Approfondimento dell'architettura di Milvus
- Modello di dati Milvus
- Il ruolo e la funzione di ogni componente di Milvus
- Elaborazione dei dati in Milvus
Interfaccia MsgStream
L'interfaccia MsgStream è fondamentale per l'elaborazione dei dati in Milvus. Quando si chiama Start()
, la coroutine in background scrive i dati nel log broker o li legge. Quando viene chiamato Close()
, la coroutine si ferma.
Interfaccia MsgStream
MsgStream può fungere da produttore e da consumatore. L'interfaccia AsProducer(channels []string)
definisce MsgStream come produttore, mentre AsConsumer(channels []string, subNamestring)
lo definisce come consumatore. Il parametro channels
è condiviso in entrambe le interfacce ed è usato per definire in quali canali (fisici) scrivere o leggere i dati.
Il numero di frammenti di una collezione può essere specificato al momento della sua creazione. Ogni frammento corrisponde a un canale virtuale (vchannel). Pertanto, una collezione può avere più canali virtuali. Milvus assegna a ogni vchannel del log broker un canale fisico (pchannel).
A ogni canale virtuale/shard corrisponde un canale fisico.
Produce()
nell'interfaccia MsgStream, incaricata di scrivere i dati nei pchannel del log broker. I dati possono essere scritti in due modi:
- Scrittura singola: le entità vengono scritte in diversi shard (vchannel) in base ai valori hash delle chiavi primarie. Poi queste entità confluiscono nei corrispondenti pcanali del log broker.
- Scrittura broadcast: le entità vengono scritte in tutti i pcanali specificati dal parametro
channels
.
Consume()
è un tipo di API bloccante. Se non ci sono dati disponibili nel pcanale specificato, la coroutine viene bloccata quando Consume()
viene chiamata nell'interfaccia MsgStream. D'altra parte, Chan()
è un'API non bloccante, il che significa che la coroutine legge ed elabora i dati solo se ci sono dati esistenti nel pchannel specificato. In caso contrario, la coroutine può elaborare altri task e non viene bloccata quando non ci sono dati disponibili.
Seek()
è un metodo per il recupero dei guasti. Quando viene avviato un nuovo nodo, è possibile ottenere il record di consumo dei dati e riprendere il consumo dei dati dal punto in cui è stato interrotto chiamando Seek()
.
Scrittura dei dati
I dati scritti nei diversi vcanali (shard) possono essere messaggi di inserimento o di cancellazione. Questi vcanali possono anche essere chiamati DmChannels (canali di manipolazione dei dati).
Collezioni diverse possono condividere gli stessi pcanali nel log broker. Una collezione può avere più shard e quindi più vcanali corrispondenti. Le entità di una stessa raccolta confluiscono di conseguenza in più canali p corrispondenti nel log broker. Di conseguenza, il vantaggio della condivisione dei canali p è un aumento del volume di throughput consentito da un'elevata concurrency del log broker.
Quando si crea una collezione, non solo si specifica il numero di shard, ma si decide anche la mappatura tra vchannels e pchannels nel log broker.
Percorso di scrittura in Milvus
Come mostrato nell'illustrazione precedente, nel percorso di scrittura, i proxy scrivono i dati nel log broker tramite l'interfaccia AsProducer()
di MsgStream. I nodi dati consumano i dati, quindi li convertono e li memorizzano nella memoria degli oggetti. Il percorso di memorizzazione è un tipo di meta-informazione che verrà registrata in etcd dai coordinatori dei dati.
Diagramma di flusso
Poiché diverse collezioni possono condividere gli stessi pcanali nel log broker, quando si consumano i dati, i nodi dati o i nodi di interrogazione devono giudicare a quale collezione appartengono i dati in un pcanale. Per risolvere questo problema, abbiamo introdotto il flowgraph in Milvus. Il suo compito principale è quello di filtrare i dati in un canale p condiviso in base agli ID delle raccolte. Quindi, possiamo dire che ogni flowgraph gestisce il flusso di dati in un corrispondente shard (vchannel) di una collezione.
Flowgraph nel percorso di scrittura
Creazione di MsgStream
Durante la scrittura dei dati, l'oggetto MsgStream viene creato nei due scenari seguenti:
- Quando il proxy riceve una richiesta di inserimento dati, cerca innanzitutto di ottenere la mappatura tra vchannels e pchannels tramite il root coordinator (root coord). Quindi il proxy crea un oggetto MsgStream.
Scenario 1
- Quando il nodo dati si avvia e legge le meta-informazioni dei canali in etcd, viene creato l'oggetto MsgStream.
Scenario 2
Leggere i dati
Percorso di lettura in Milvus
Il flusso di lavoro generale della lettura dei dati è illustrato nell'immagine qui sopra. Le richieste di interrogazione vengono trasmesse tramite DqRequestChannel ai nodi di interrogazione. I nodi di interrogazione eseguono le attività di interrogazione in parallelo. I risultati delle query passano attraverso gRPC e il proxy aggrega i risultati e li restituisce al client.
Per dare un'occhiata più da vicino al processo di lettura dei dati, possiamo vedere che il proxy scrive le richieste di query in DqRequestChannel. I nodi di interrogazione consumano quindi i messaggi sottoscrivendo il DqRequestChannel. Ogni messaggio nel DqRequestChannel viene trasmesso in broadcast, in modo che tutti i nodi di interrogazione sottoscritti possano riceverlo.
Quando i nodi di interrogazione ricevono le richieste di interrogazione, eseguono un'interrogazione locale sia sui dati batch memorizzati in segmenti sigillati, sia sui dati in streaming inseriti dinamicamente in Milvus e memorizzati in segmenti in crescita. In seguito, i nodi di interrogazione devono aggregare i risultati dell'interrogazione in entrambi i segmenti sigillati e in crescita. Questi risultati aggregati vengono passati al proxy tramite gRPC.
Il proxy raccoglie tutti i risultati da più nodi di interrogazione e li aggrega per ottenere i risultati finali. Quindi il proxy restituisce i risultati finali della query al client. Poiché ogni richiesta di query e i relativi risultati sono contrassegnati dallo stesso requestID univoco, il proxy può capire quali risultati corrispondono a quale richiesta di query.
Diagramma di flusso
Diagramma di flusso nel percorso di lettura
Analogamente al percorso di scrittura, i diagrammi di flusso sono introdotti anche nel percorso di lettura. Milvus implementa l'architettura Lambda unificata, che integra l'elaborazione dei dati incrementali e storici. Pertanto, i nodi di interrogazione devono ottenere anche dati in streaming in tempo reale. Allo stesso modo, i flowgraph nel percorso di lettura filtrano e differenziano i dati provenienti da raccolte diverse.
Creazione di MsgStream
Creazione dell'oggetto MsgStream nel percorso di lettura
Durante la lettura dei dati, l'oggetto MsgStream viene creato nel seguente scenario:
- In Milvus, i dati non possono essere letti se non vengono caricati. Quando il proxy riceve una richiesta di caricamento dei dati, la invia al coordinatore delle interrogazioni, che decide come assegnare gli shard ai diversi nodi di interrogazione. Le informazioni di assegnazione (cioè i nomi dei vcanali e la mappatura tra i vcanali e i corrispondenti pcanali) vengono inviate ai nodi di interrogazione tramite chiamata di metodo o RPC (remote procedure call). Successivamente, i nodi di interrogazione creano gli oggetti MsgStream corrispondenti per consumare i dati.
Operazioni DDL
DDL è l'acronimo di data definition language. Le operazioni DDL sui metadati possono essere classificate in richieste di scrittura e richieste di lettura. Tuttavia, questi due tipi di richieste sono trattati allo stesso modo durante l'elaborazione dei metadati.
Le richieste di lettura sui metadati includono:
- Schema di raccolta delle query
- Informazioni sull'indicizzazione delle query e altro ancora
Le richieste di scrittura includono
- Creare una raccolta
- Eliminare una raccolta
- Creare un indice
- Eliminare un indice e altro ancora
Le richieste DDL vengono inviate al proxy dal client e il proxy le trasmette nell'ordine ricevuto al root coord, che assegna un timestamp per ogni richiesta DDL e conduce controlli dinamici sulle richieste. Il proxy gestisce ogni richiesta in modo seriale, cioè una richiesta DDL alla volta. Il proxy non elaborerà la richiesta successiva finché non avrà completato l'elaborazione della richiesta precedente e non avrà ricevuto i risultati dal root coord.
Operazioni DDL.
Come mostrato nell'illustrazione precedente, ci sono K
richieste DDL nella coda dei task di Root coord. Le richieste DDL nella coda dei task sono disposte nell'ordine in cui sono state ricevute dal root coord. Quindi, ddl1
è la prima inviata al root coord e ddlK
è l'ultima di questo gruppo. Il root coord elabora le richieste una per una nell'ordine temporale.
In un sistema distribuito, la comunicazione tra i proxy e il root coord è abilitata da gRPC. Il root coord tiene un registro del valore massimo del timestamp dei task eseguiti per garantire che tutte le richieste DDL siano elaborate in ordine temporale.
Si supponga che esistano due proxy indipendenti, il proxy 1 e il proxy 2. Entrambi inviano richieste DDL al sistema di gestione dei dati. Entrambi inviano richieste DDL alla stessa root coord. Tuttavia, un problema è che le richieste precedenti non vengono necessariamente inviate alla root coord prima di quelle ricevute da un altro proxy più tardi. Ad esempio, nell'immagine precedente, quando DDL_K-1
viene inviato al root coord dal proxy 1, DDL_K
dal proxy 2 è già stato accettato ed eseguito dal root coord. Come registrato dal root coord, il valore massimo del timestamp dei task eseguiti a questo punto è K
. Quindi, per non interrompere l'ordine temporale, la richiesta DDL_K-1
sarà rifiutata dalla coda dei task del coord root. Tuttavia, se il proxy 2 invia la richiesta DDL_K+5
al coord. radice a questo punto, la richiesta sarà accettata nella coda dei task e sarà eseguita successivamente in base al suo valore di timestamp.
Indicizzazione
Creazione di un indice
Quando riceve le richieste di costruzione di un indice dal client, il proxy esegue innanzitutto dei controlli statici sulle richieste e le invia al root coord. Quindi il coord root persiste queste richieste di costruzione dell'indice nel meta storage (etcd) e invia le richieste al coordinatore dell'indice (index coord).
Costruzione di un indice.
Come illustrato in precedenza, quando il coordinatore dell'indice riceve richieste di costruzione dell'indice dal coordinatore principale, per prima cosa persiste l'attività in etcd per il meta store. Lo stato iniziale dell'attività di costruzione dell'indice è Unissued
. Il coord indice mantiene un registro del carico di attività di ogni nodo indice e invia le attività in entrata a un nodo indice meno carico. Al completamento dell'attività, il nodo indice scrive lo stato dell'attività, Finished
o Failed
, nella meta memoria, che è etcd in Milvus. In seguito, il nodo indice capirà se il compito di costruzione dell'indice è riuscito o fallito cercando in etcd. Se il compito fallisce a causa di risorse di sistema limitate o dell'abbandono del nodo indice, l'index coord riattiva l'intero processo e assegna lo stesso compito a un altro nodo indice.
Abbandono di un indice
Inoltre, l'index coord è anche responsabile delle richieste di abbandono degli indici.
Abbandono di un indice.
Quando il root coord riceve una richiesta di abbandono di un indice da parte del client, per prima cosa contrassegna l'indice come "abbandonato" e restituisce il risultato al client, notificandolo al coord indice. Quindi il coord indice filtra tutte le attività di indicizzazione con IndexID
e quelle che soddisfano la condizione vengono abbandonate.
La coroutine in background dell'index coord eliminerà gradualmente tutti i task di indicizzazione contrassegnati come "dropped" dallo storage degli oggetti (MinIO e S3). Questo processo coinvolge l'interfaccia recycleIndexFiles. Quando tutti i file di indice corrispondenti vengono eliminati, le metainformazioni dei task di indicizzazione eliminati vengono rimosse dal meta storage (etcd).
Informazioni sulla serie Deep Dive
Con l'annuncio ufficiale della disponibilità generale di Milvus 2.0, abbiamo organizzato questa serie di blog Milvus Deep Dive per fornire un'interpretazione approfondita dell'architettura e del codice sorgente di Milvus. Gli argomenti trattati in questa serie di blog includono:
- Interfaccia MsgStream
- Scrittura dei dati
- Leggere i dati
- Operazioni DDL
- Indicizzazione
- Informazioni sulla serie 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