Anatomia di un sistema di gestione di database vettoriale cloud native
Sono onorato che il nostro ultimo paper, "Manu: A Cloud Native Vector Database Management System" sia stato accettato da VLDB'22, una delle principali conferenze internazionali nella ricerca sui database. In questo articolo discuterò la filosofia e i principi chiave di progettazione alla base di Manu (nome del progetto per Milvus 2.0), un database cloud native creato appositamente per la gestione dei dati vettoriali. Puoi consultare i nostri paper precedenti e il nostro repository GitHub per maggiori informazioni.
Contesto
Quando abbiamo progettato Milvus 1.0, il nostro obiettivo principale era semplicemente supportare la gestione dei vettori e ottimizzare le prestazioni del recupero vettoriale. Ma, interagendo con un numero sempre maggiore di utenti nel corso degli anni, abbiamo scoperto alcuni requisiti aziendali comuni per i database vettoriali che erano difficili da soddisfare con il framework iniziale.
Queste esigenze possono essere raggruppate nelle seguenti categorie: requisiti in continua evoluzione, una policy di coerenza più flessibile, elasticità a livello di componente e un modello di elaborazione delle transazioni più semplice ed efficiente.
Requisiti in continua evoluzione
I requisiti aziendali non sono ancora completamente definiti per quanto riguarda l'elaborazione dei dati vettoriali.
Nei primi tempi, la ricerca dei K-nearest neighbor era la più richiesta. Ma poi sono emersi sempre più requisiti, tra cui la ricerca per intervallo, il supporto per varie metriche di distanza personalizzate, la ricerca ibrida, le query multimodali e altre semantiche di query sempre più diverse.
Questo richiede che l'architettura di un database vettoriale sia sufficientemente flessibile da supportare nuovi requisiti in modo rapido e agile.
Una policy di coerenza più flessibile
Prendiamo come esempio la raccomandazione di contenuti: questo scenario ha requisiti elevati in termini di tempestività. I nuovi contenuti devono essere consigliati agli utenti entro pochi minuti o persino secondi, quindi il sistema non può impiegare un giorno o più per aggiornare le sue raccomandazioni. In questi scenari, è difficile garantire i risultati aziendali fornendo solo coerenza eventuale, mentre ci sarebbe un grande overhead di sistema se insistessimo sulla coerenza forte.
Per affrontare questo problema, proponiamo la seguente soluzione: in base ai requisiti aziendali, gli utenti possono specificare il ritardo massimo tollerabile prima che i dati inseriti possano essere interrogati. Il sistema, a sua volta, regola determinati meccanismi di elaborazione dei dati per garantire il risultato finale per l'azienda.
Elasticità a livello di componente
I requisiti di risorse e l'intensità del carico variano notevolmente per ciascun componente di un database vettoriale in applicazioni diverse. Ad esempio, i componenti di recupero vettoriale e query richiedono grandi risorse computazionali e di memoria per garantire le loro prestazioni, mentre l'archiviazione dei dati e la gestione dei metadati necessitano solo di poche risorse per funzionare.
In termini di applicazioni, il requisito più critico per i sistemi di raccomandazione è la capacità di effettuare query concorrenti su larga scala e, quindi, in questi sistemi solo il componente di query sostiene un carico più elevato. Le applicazioni di analytics, d'altra parte, spesso devono importare una grande quantità di dati offline, quindi la pressione del carico ricade sull'inserimento dei dati e sulla costruzione degli indici, due componenti interconnessi.
Per migliorare l'utilizzo delle risorse, è necessario che ogni modulo funzionale abbia scalabilità indipendente ed elastica, in modo che l'allocazione delle risorse del sistema possa essere più strettamente allineata alle esigenze effettive di un'applicazione.
Un modello di elaborazione delle transazioni più semplice ed efficiente
A rigor di termini, il modello di transazione è uno spazio di ottimizzazione che può essere sfruttato nella progettazione del sistema piuttosto che un requisito aziendale.
Con l’evoluzione del machine learning nella sua capacità descrittiva, le aziende tendono a fondere dati provenienti da più dimensioni di una singola entità per rappresentarla come un unico vettore unificato. Ad esempio, nella profilazione degli utenti, informazioni come profili personali, preferenze e relazioni sociali vengono fuse insieme. Di conseguenza, i database vettoriali possono essere mantenuti con una sola tabella senza dover implementare operazioni simili a JOIN, comuni nei database tradizionali. In questo modo, il sistema deve supportare solo ACID a livello di riga su una singola tabella e può fare a meno di transazioni complesse che coinvolgono più tabelle, lasciando ampio spazio al disaccoppiamento dei componenti e all’ottimizzazione delle prestazioni nel sistema.
Obiettivi
Come seconda major release di Milvus, Manu è posizionato come un sistema di database vettoriale distribuito e cloud-native.
Quando abbiamo iniziato a progettare Manu, abbiamo considerato i vari requisiti aziendali menzionati sopra e li abbiamo combinati con i requisiti comuni per un sistema distribuito. Il risultato sono cinque obiettivi generali per Manu: evolvibilità a lungo termine, consistenza regolabile, buona elasticità, alta disponibilità e alte prestazioni.
Evolvibilità a lungo termine
Per controllare la complessità del sistema a un livello gestibile mentre le funzionalità evolvono, dobbiamo disaccoppiare bene il sistema per garantire che i singoli componenti possano evolvere, essere aggiunti o essere sostituiti indipendentemente, con interferenze minime sugli altri componenti.
Consistenza regolabile
Il sistema deve supportare la consistenza delta in modo che gli utenti possano specificare il ritardo di visibilità delle query per i dati appena inseriti. La consistenza delta richiede che tutte le query possano restituire tutti i dati pertinenti almeno fino all’unità di tempo delta, che può essere specificata dall’applicazione utente in base ai requisiti aziendali.
Buona elasticità
Per migliorare l’efficienza dell’utilizzo delle risorse, il sistema deve ottenere un’elasticità a grana fine a livello di componente, nonché una politica di allocazione delle risorse che tenga conto delle varie dipendenze hardware dei componenti.
Alta disponibilità e prestazioni
L’alta disponibilità è il requisito fondamentale di tutti i database cloud, il che richiede che, nel caso in cui alcuni nodi di servizio o componenti si guastino, gli altri servizi non siano interessati e che il sistema sia capace di un efficace ripristino dai guasti.
Le alte prestazioni sono un cliché per i database vettoriali. Nel processo di progettazione, dobbiamo controllare rigorosamente l’overhead generato a livello di framework del sistema per garantire buone prestazioni.
Architettura di Manu
Manu adotta un’architettura a quattro livelli che consente il disaccoppiamento della lettura dalla scrittura, dello stateless dallo stateful e dello storage dal computing.
Come mostrato nella figura sotto, dall’alto verso il basso, Manu ha quattro livelli, cioè livello di accesso, livello di coordinamento, livello worker e livello di storage. Manu utilizza anche un sistema di log come sua dorsale, che collega i componenti disaccoppiati.
Architettura di Manu.
Livello di accesso
Il livello di accesso è costituito da proxy stateless che fungono da endpoint utente.
Questi proxy ricevono richieste dai client, distribuiscono le richieste ai componenti corrispondenti e raccolgono i risultati prima di restituirli ai client. Inoltre, i proxy memorizzano nella cache una copia dei metadati per verificare la legittimità delle richieste di ricerca (ad esempio, se la collection da cercare esiste).
Livello di coordinamento
Il livello di coordinamento gestisce lo stato del sistema, mantiene i metadati e coordina i componenti del sistema per l’elaborazione delle attività.
Esistono quattro tipi di coordinatori, ciascuno progettato indipendentemente per funzionalità diverse. In questo modo, i guasti del sistema possono essere isolati e i componenti possono evolvere separatamente. Per ragioni di affidabilità, ogni coordinatore può avere più istanze (ad esempio, una principale e due di backup).
Coordinatore root
Il coordinatore root gestisce le richieste di definizione dei dati, come la creazione/eliminazione di raccolte, e mantiene le meta-informazioni delle raccolte (ad es., le proprietà delle raccolte, il tipo di dati di ciascuna proprietà).
Coordinatore dei dati
Il coordinatore dei dati si occupa della persistenza dei dati. Coordina i nodi dati per trasformare le richieste di aggiornamento dei dati in binlog e registra le informazioni dettagliate delle raccolte (ad es., l'elenco dei segmenti di ciascuna raccolta, il percorso di archiviazione di ciascun segmento).
Coordinatore degli indici
Il coordinatore degli indici gestisce l'indicizzazione dei dati. Coordina i nodi indice per le attività di indicizzazione e registra le informazioni sugli indici di ciascuna raccolta (ad es., tipo di indice, parametri correlati, percorso di archiviazione, ecc.).
Coordinatore delle query
Il coordinatore delle query monitora lo stato dei nodi query e regola l'assegnazione dei segmenti (insieme agli indici correlati) ai nodi query per il bilanciamento del carico.
Livello worker
Il livello worker esegue le molteplici attività nel sistema.
Tutti i nodi worker sono stateless: recuperano copie di sola lettura dei dati per svolgere le attività e non devono coordinarsi tra loro. Pertanto, il numero di nodi worker può essere regolato in modo flessibile in base al carico. Inoltre, Manu utilizza nodi worker diversi per attività diverse, in modo che ogni tipo di nodo possa essere scalato indipendentemente in base al carico effettivo e ai requisiti di QoS.
Livello di storage
Il livello di storage archivia in modo persistente le informazioni sullo stato del sistema, i metadati, le raccolte e gli indici associati.
Manu utilizza store KV (key-value) distribuiti ad alta disponibilità, come etcd, per archiviare le informazioni sullo stato del sistema e i metadati. Quando i metadati vengono aggiornati, i dati verranno prima scritti nello store KV e poi sincronizzati con i coordinatori pertinenti. I dati di grande volume, come quelli delle raccolte e degli indici, sono gestiti con servizi di object storage come AWS S3. L'elevata latenza associata all'object storage non costituisce un collo di bottiglia per le prestazioni, perché i nodi worker prelevano copie di sola lettura dei dati dall'object store e le memorizzano nella cache locale prima di elaborare i dati, quindi la maggior parte dell'elaborazione dei dati viene eseguita localmente.
Backbone dei log
Backbone dei log.
Per disaccoppiare meglio i componenti del sistema (ad es., WAL, binlog, nodi dati, nodi indice e nodi query), in modo che ciascuno possa essere scalato ed evolvere indipendentemente, Manu segue il paradigma "log as data" e utilizza un sistema di log come proprio backbone, che collega i componenti di sistema disaccoppiati. In Manu, i log possono essere sottoscritti in modo persistente da diversi componenti del sistema, che quindi sono chiamati sottoscrittori dei log.
I log in Manu possono essere suddivisi in write-ahead log (WAL) e binlog. Il WAL è la parte incrementale del log di sistema, mentre il binlog è la parte di base. Si completano a vicenda in termini di ritardo, capacità e costo.
Come mostrato nella figura sopra, i logger sono i punti di ingresso del sistema di log e pubblicano i dati sul WAL. I nodi dati sottoscrivono il WAL e convertono i WAL basati su righe in binlog basati su colonne. Tutti i componenti di sola lettura, come i nodi indice e i nodi query, sono sottoscrittori indipendenti del servizio di log per mantenersi aggiornati.
Il sistema di log serve anche a trasmettere messaggi tra componenti. In altre parole, i componenti possono trasmettere eventi di sistema tramite i log. Ad esempio, i nodi dati possono informare gli altri componenti su quali segmenti sono stati scritti nell'object storage e i nodi indice possono informare tutti i coordinatori delle query non appena sono stati creati nuovi indici. Inoltre, tipi diversi di messaggi sono organizzati su canali diversi. Ogni componente deve solo sottoscrivere il proprio canale corrispondente invece di ascoltare tutti i log trasmessi.
Workflow di elaborazione dei dati
Questa sezione approfondisce il workflow di elaborazione dei dati all'interno di Manu e introduce il processo di inserimento dei dati, creazione degli indici ed esecuzione delle query.
Inserimento dei dati
Flusso di lavoro dell'inserimento dei dati.
La figura sopra illustra il flusso di lavoro dell'inserimento dei dati in Manu e i componenti pertinenti coinvolti.
Dopo essere state elaborate dal proxy, le richieste di inserimento dei dati vengono distribuite in diversi bucket in base ad algoritmi di hash. In genere, nel sistema Manu sono presenti più logger che gestiscono le entità in ciascun bucket di hash sulla base dell'hashing coerente. Le entità in ciascun bucket di hash vengono scritte in un canale write-ahead log (WAL) che mappa solo a questo bucket. Quando un logger riceve una richiesta di inserimento dei dati, assegna a questa richiesta un log sequence number (LSN) globalmente univoco e la scrive nel canale WAL corrispondente. L'LSN viene generato dall'oracolo del servizio centrale del tempo (TSO). Ogni logger deve ricevere un LSN dal TSO e salvare l'LSN localmente a intervalli regolari.
Per garantire che il pub/sub dei log abbia un basso ritardo e sia fine-grained, le entità vengono archiviate in modo row-based nel WAL in Manu, e ogni componente che si sottoscrive al WAL legge i dati da esso in modalità streaming. Nella maggior parte dei casi, il WAL può essere implementato tramite una coda di messaggi basata su cloud come Kafka o Pulsar. I nodi dati si sottoscrivono ai WAL e convertono i WAL row-based in binlog column-based. La natura column-based del binlog rende facile comprimere e accedere ai dati. Un esempio di questa efficienza riguarda i nodi indice. I nodi indice leggono solo la colonna vettoriale richiesta dal binlog per la costruzione dell'indice e sono quindi esenti da amplificazioni di lettura.
Costruzione dell'indice
Esistono due scenari di costruzione dell'indice in Manu: indicizzazione batch e indicizzazione stream. L'indicizzazione batch si verifica quando l'utente costruisce un indice per un'intera raccolta (ad esempio, quando tutti i vettori vengono aggiornati con un nuovo modello di embedding). In questo caso, il coordinatore degli indici ottiene dal coordinatore dei dati i percorsi di tutti i segmenti nella raccolta e istruisce i nodi indice a costruire un indice per ciascun segmento. L'indicizzazione stream avviene quando gli utenti inseriscono continuamente nuove entità e gli indici vengono costruiti in modo asincrono al volo senza interrompere i servizi di ricerca.
Quando il nodo dati scrive un nuovo segmento nel binlog, il coordinatore dei dati notifica al coordinatore degli indici di creare un'attività per un nodo indice affinché costruisca un indice sul nuovo segmento. In entrambi gli scenari di indicizzazione batch e stream, dopo che l'indice richiesto è stato costruito per un segmento, il nodo indice lo rende persistente nell'object storage e invia il percorso di archiviazione al coordinatore degli indici, notificando il coordinatore delle query in modo che i nodi query possano caricare l'indice per elaborare le query.
Esecuzione delle query
Manu partiziona una raccolta in segmenti e distribuisce i segmenti tra i nodi query per eseguire le richieste di query in parallelo. I proxy memorizzano nella cache una copia della distribuzione dei segmenti sui nodi query interrogando il coordinatore delle query e inoltrano le richieste di ricerca ai nodi query che contengono segmenti della raccolta cercata. I nodi query eseguono query vettoriali sui propri segmenti locali, uniscono i risultati e li restituiscono al proxy. Il proxy aggrega ulteriormente i risultati di ciascun nodo query e restituisce i risultati finali al client.
I nodi query ottengono i dati da tre fonti: il WAL, i file di indice e il binlog. Per i dati storici, i nodi query leggono i binlog o i file di indice corrispondenti dall'object storage. Per i dati incrementali, invece, i nodi query leggono direttamente dal WAL in modalità streaming. Ottenere i dati incrementali dal binlog causerà latenza nella visibilità dei dati, il che è particolarmente vero per le richieste di ricerca di grandi dimensioni. In altre parole, i dati appena inseriti saranno disponibili per le query solo dopo un lungo periodo di tempo, il che non riesce a soddisfare l'esigenza di alta coerenza in alcuni scenari.
Come accennato in precedenza, Manu adotta un modello di consistenza delta per consentire agli utenti di regolare i livelli di consistenza in modo più flessibile. La consistenza delta garantisce che i dati aggiornati (inclusi i dati inseriti ed eliminati) possano essere interrogati e ricercati fino a delta unità di tempo dopo che la richiesta di aggiornamento dei dati è stata ricevuta da Manu.
Manu ottiene la consistenza delta aggiungendo LSN con timestamp a tutte le richieste di inserimento dati e query. Durante l’esecuzione delle richieste di query, il nodo di query controlla il timestamp della richiesta (Lr) e il timestamp dell’ultima richiesta di aggiornamento dati elaborata dal nodo di query (Ls). La richiesta di query viene eseguita solo quando l’intervallo tra Lr e Ls è inferiore a delta. In caso contrario, la richiesta di query attende di essere eseguita finché gli aggiornamenti dei dati registrati nel WAL non vengono elaborati. Tuttavia, se non vi è alcun aggiornamento dei dati per un lungo periodo di tempo, l’intervallo di tempo tra Ls e l’ora di sistema corrente diventerà così piccolo che le query vengono bloccate. Per prevenire tale problema, Manu inserisce regolarmente informazioni di controllo nel WAL, costringendo il nodo di query ad aggiornare il proprio timestamp.
Valutazione delle prestazioni
Nel paper, abbiamo anche integrato Manu in applicazioni reali e condotto una valutazione complessiva delle prestazioni del sistema. Di seguito sono riportati parte dei risultati della valutazione.
Prestazioni di query di Manu e di altri sistemi di ricerca vettoriale.
La figura sopra confronta Manu con altri quattro sistemi di ricerca vettoriale open-source anonimi in termini di prestazioni di query. Possiamo vedere che Manu supera evidentemente altri sistemi di ricerca vettoriale quando esegue query sui dataset SIFT e DEEP.
Prestazioni di query di Manu con diversi numeri di nodi.
La figura sopra mostra le prestazioni di query di Manu quando il numero di nodi di query varia. Possiamo vedere che quando si interrogano diversi dataset con diverse metriche di similarità, le prestazioni di query di Manu presentano una relazione approssimativamente lineare con il numero di nodi di query.
Prestazioni di query di Manu con diversi livelli di consistenza.
Le figure sopra dimostrano le prestazioni di query di Manu con diversi livelli di consistenza. Le coordinate orizzontali rappresentano i valori di delta come nella consistenza delta. Ogni figura riflette la frequenza delle informazioni di controllo inviate al WAL che costringono i nodi di query a sincronizzare l’ora. Possiamo vedere dalla figura che la latenza di query di Manu diminuisce drasticamente all’aumentare del valore di delta. Pertanto, gli utenti di Manu devono scegliere il valore delta appropriato in base alla loro esigenza di prestazioni e consistenza.
Conclusione
In questo paper, sulla base dei requisiti reali per i database vettoriali, abbiamo introdotto i design di Manu e i workflow delle sue funzionalità principali. In breve, le due caratteristiche principali di Manu sono le seguenti:
Manu utilizza la backbone di log per connettere i componenti del sistema, il che consente la scalabilità e l’evoluzione indipendenti di ciascun componente e facilita l’allocazione delle risorse e l’isolamento dei guasti.
Con il sistema di log e l’LSN, Manu adotta un modello di consistenza delta per consentire un trade-off flessibile tra consistenza, costo e prestazioni.
In sintesi, il contributo principale del nostro paper VLDB risiede nell’introduzione della domanda reale di database vettoriali e nella progettazione dell’architettura di base di un database vettoriale cloud-native. Attualmente, l’architettura è ancora lontana dall’essere perfetta e alcune delle nostre direzioni future includono:
Come recuperare vettori estratti da contenuti multimodali;
Come sfruttare meglio i servizi di archiviazione cloud, inclusi dischi locali, unità cloud e altri servizi di archiviazione, per rendere il recupero dei dati più efficiente;
Come massimizzare le prestazioni di indicizzazione e ricerca con l’aiuto di nuovi hardware di elaborazione, archiviazione o comunicazione come FPGA、GPU、RDMA、NVM 、RDMA.
Nota finale
Un anno fa, ho partecipato a ACM SIGMOD 2021 a Xi'an con Charles Xie, CEO di Zilliz. L'idea di scrivere questo articolo mi è venuta mentre stavamo tornando a Shanghai per la GA release di Milvus 2.0 (Manu). Sia Charles sia io abbiamo percepito che i database cloud-native stavano diventando il nuovo tema caldo in ambito accademico. È stata una tale coincidenza che Manu sia esattamente un sistema di database cloud-native e purpose-built per vettori massivi. Di conseguenza, siamo arrivati a scrivere questo articolo su Manu e sul sistema di gestione di database cloud-native.
Speriamo che il nostro articolo possa far luce e attrarre più studiosi e colleghi del settore a unirsi a noi nell'esplorazione e nella ricerca sui sistemi di gestione di database vettoriali cloud-native.
Vogliamo anche esprimere la nostra gratitudine al Professore assistente Bo Tang, al Professore assistente di ricerca Xiao Yan e a Long Xiang per il loro contributo. Questo articolo è scritto congiuntamente dal team di Zilliz e dal Database Group della Southern University of Science and Technology.
Continua a leggere

Milvus 2.6.x Now Generally Available on Zilliz Cloud, Making Vector Search Faster, Smarter, and More Cost-Efficient for Production AI
Milvus 2.6.x is now GA on Zilliz Cloud, delivering faster vector search, smarter hybrid queries, and lower costs for production RAG and AI applications.

Zilliz Cloud Update: Tiered Storage, Business Critical Plan, Cross-Region Backup, and Pricing Changes
This release offers a rebuilt tiered storage with lower costs, a new Business Critical plan for enhanced security, and pricing updates, among other features.

1 Table = 1000 Words? Foundation Models for Tabular Data
TableGPT2 automates tabular data insights, overcoming schema variability, while Milvus accelerates vector search for efficient, scalable decision-making.



