🚀 Prova Zilliz Cloud, la versione completamente gestita di Milvus, gratuitamente—sperimenta prestazioni 10 volte più veloci! Prova Ora>>

milvus-logo
LFAI

Preparazione

  • Engineering
April 13, 2020
milvus

In questo articolo descriveremo principalmente come i dati vettoriali vengono registrati nella memoria di Milvus e come vengono mantenuti.

Di seguito sono riportati i nostri principali obiettivi di progettazione:

  1. L'efficienza dell'importazione dei dati deve essere elevata.
  2. I dati possono essere visualizzati il prima possibile dopo l'importazione.
  3. Evitare la frammentazione dei file di dati.

Per questo motivo, abbiamo creato un buffer di memoria (insert buffer) per inserire i dati, in modo da ridurre il numero di context switch di IO casuali sul disco e sul sistema operativo e migliorare le prestazioni dell'inserimento dei dati. L'architettura di memoria basata su MemTable e MemTableFile ci permette di gestire e serializzare i dati in modo più pratico. Lo stato del buffer è suddiviso in Mutabile e Immutabile, il che consente di persistere i dati su disco mantenendo i servizi esterni disponibili.

Preparazione

Quando l'utente è pronto a inserire un vettore in Milvus, deve prima creare una Collection (* Milvus rinomina Table in Collection nella versione 0.7.0). La collezione è l'unità di base per la registrazione e la ricerca di vettori in Milvus.

Ogni collezione ha un nome unico e alcune proprietà che possono essere impostate; i vettori vengono inseriti o ricercati in base al nome della collezione. Quando si crea una nuova collezione, Milvus ne registra le informazioni nei metadati.

Inserimento dei dati

Quando l'utente invia una richiesta di inserimento di dati, questi vengono serializzati e deserializzati per raggiungere il server Milvus. I dati vengono ora scritti in memoria. La scrittura in memoria è suddivisa grossomodo nelle seguenti fasi:

2-data-insertion-milvus.png 2-inserimento-dati-milvus.png

  1. In MemManager, trovare o creare una nuova MemTable corrispondente al nome della collezione. Ogni MemTable corrisponde a un buffer della Collezione in memoria.
  2. Una MemTable conterrà uno o più MemTableFile. Ogni volta che creiamo un nuovo MemTableFile, registriamo contemporaneamente questa informazione nella Meta. Dividiamo i MemTableFile in due stati: Mutabile e Immutabile. Quando la dimensione di MemTableFile raggiunge la soglia, diventa Immutabile. Ogni MemTable può avere un solo MemTableFile mutabile da scrivere in qualsiasi momento.
  3. I dati di ciascun MemTableFile saranno infine registrati in memoria nel formato del tipo di indice impostato. Il MemTableFile è l'unità di base per la gestione dei dati in memoria.
  4. In qualsiasi momento, l'utilizzo della memoria per i dati inseriti non supererà il valore preimpostato (insert_buffer_size). Questo perché a ogni richiesta di inserimento di dati, MemManager può facilmente calcolare la memoria occupata dai MemTableFile contenuti in ogni MemTable, e quindi coordinare la richiesta di inserimento in base alla memoria corrente.

Grazie all'architettura multilivello di MemManager, MemTable e MemTableFile, l'inserimento dei dati può essere gestito e mantenuto meglio. Naturalmente, possono fare molto di più.

Interrogazione quasi in tempo reale

In Milvus è sufficiente attendere un secondo al massimo perché i dati inseriti si spostino dalla memoria al disco. L'intero processo può essere approssimativamente riassunto dalla seguente immagine:

2-near-real-time-query-milvus.png 2-query in tempo quasi reale-milvus.png

Innanzitutto, i dati inseriti entrano in un buffer di inserimento in memoria. Il buffer passa periodicamente dallo stato iniziale Mutable allo stato Immutable, in preparazione alla serializzazione. Quindi, questi buffer immutabili saranno serializzati su disco periodicamente dal thread di serializzazione in background. Dopo che i dati sono stati inseriti, le informazioni sull'ordine saranno registrate nei metadati. A questo punto, i dati possono essere ricercati!

Ora descriveremo in dettaglio i passaggi della figura.

Conosciamo già il processo di inserimento dei dati nel buffer mutabile. Il passo successivo consiste nel passare dal buffer mutabile al buffer immutabile:

3-mutable-buffer-immutable-buffer-milvus.png 3-mutable-buffer-immutable-buffer-milvus.png

La coda immutabile fornirà al thread di serializzazione in background lo stato immutabile e il MemTableFile pronto per essere serializzato. Ogni MemTable gestisce la propria coda immutabile e quando la dimensione dell'unico MemTableFile mutabile della MemTable raggiunge la soglia, entra nella coda immutabile. Un thread in background responsabile di ToImmutable estrae periodicamente tutti i MemTableFile nella coda immutabile gestita da MemTable e li invia alla coda immutabile totale. Va notato che le due operazioni di scrittura dei dati in memoria e di modifica dei dati in memoria in uno stato che non può essere scritto non possono avvenire contemporaneamente, ed è necessario un blocco comune. Tuttavia, l'operazione di ToImmutable è molto semplice e non causa quasi alcun ritardo, quindi l'impatto sulle prestazioni dei dati inseriti è minimo.

Il passo successivo consiste nel serializzare su disco il file MemTableFile nella coda di serializzazione. Questa operazione è suddivisa principalmente in tre fasi:

4-serialize-memtablefile-milvus.png 4-serializzare-memtablefile-milvus.png

Innanzitutto, il thread di serializzazione in background estrae periodicamente i MemTableFile dalla coda immutabile. Quindi, vengono serializzati in file grezzi di dimensioni fisse (Raw TableFiles). Infine, registriamo queste informazioni nei metadati. Quando effettuiamo una ricerca vettoriale, interroghiamo il TableFile corrispondente nei metadati. Da qui, questi dati possono essere ricercati!

Inoltre, in base all'index_file_size impostato, dopo che il thread di serializzazione ha completato un ciclo di serializzazione, unirà alcuni TableFile a dimensione fissa in un TableFile e registrerà anche queste informazioni nei metadati. A questo punto, il TableFile può essere indicizzato. Anche la costruzione dell'indice è asincrona. Un altro thread in background responsabile della costruzione dell'indice leggerà periodicamente il TableFile nello stato ToIndex dei metadati per eseguire la costruzione dell'indice corrispondente.

In realtà, con l'aiuto di TableFile e metadati, la ricerca vettoriale diventa più intuitiva e conveniente. In generale, è necessario ottenere dai metadati i TableFile corrispondenti alla Collection interrogata, cercare in ogni TableFile e infine unire. In questo articolo non ci addentriamo nell'implementazione specifica della ricerca.

Se volete saperne di più, leggete il nostro codice sorgente o gli altri articoli tecnici su Milvus!

Try Managed Milvus for Free

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

Get Started

Like the article? Spread the word

Continua a Leggere