🚀 Experimente o Zilliz Cloud, o Milvus totalmente gerenciado, gratuitamente—experimente um desempenho 10x mais rápido! Experimente Agora>>

milvus-logo
LFAI

HomeBlogsEvolução da base de dados vetorial Milvus escalável na nuvem

Evolução da base de dados vetorial Milvus escalável na nuvem

  • Engineering
December 21, 2021
Jun Gu

Neste artigo, partilharemos o processo de reflexão sobre a forma como concebemos a nova arquitetura do cluster da base de dados Milvus.

Objectivos da base de dados vetorial Milvus

Quando a ideia da base de dados vetorial Milvus surgiu pela primeira vez, queríamos construir uma infraestrutura de dados que pudesse ajudar as pessoas a acelerar a adoção da IA nas suas organizações.

Para cumprir esta missão, definimos dois objectivos cruciais para o projeto Milvus.

Facilidade de utilização

A IA/ML é uma área emergente onde estão sempre a surgir novas tecnologias. A maioria dos programadores não está totalmente familiarizada com as tecnologias e ferramentas de IA em rápido crescimento. Os programadores já gastaram a maior parte das suas energias a encontrar, treinar e afinar os modelos. É-lhes difícil despender esforços adicionais para lidar com as grandes quantidades de vectores de incorporação gerados pelos modelos. Para além disso, a manipulação de um grande volume de dados é sempre uma tarefa muito difícil.

Assim, atribuímos uma prioridade muito elevada à "facilidade de utilização", uma vez que pode reduzir significativamente o custo de desenvolvimento.

Baixos custos de funcionamento

Um dos principais obstáculos da IA na produção é justificar o retorno do investimento. Teríamos mais oportunidades de colocar as nossas aplicações de IA em produção com custos de funcionamento mais baixos. E isso contribuiria para aumentar a margem de benefícios potenciais.

Princípios de conceção do Milvus 2.0

Começámos por atingir estes objectivos no Milvus 1.0. Mas está longe de ser suficiente, especialmente em termos de escalabilidade e disponibilidade. Iniciámos então o desenvolvimento do Milvus 2.0 para melhorar estes pontos. Os princípios que estabelecemos para esta nova versão incluem:

  • Ter como objetivo uma elevada escalabilidade e disponibilidade
  • Basear-se em infra-estruturas e práticas de nuvem maduras
  • Compromisso mínimo de desempenho na nuvem

Por outras palavras, queremos tornar o cluster de bases de dados Milvus nativo da nuvem.

A evolução dos clusters de bases de dados

A base de dados vetorial é uma nova espécie de base de dados, uma vez que lida com novos tipos de dados (vectores). Mas continua a partilhar os mesmos desafios que as outras bases de dados, com alguns dos seus próprios requisitos. No resto deste artigo, vou centrar-me no que aprendemos com as implementações de clusters de bases de dados existentes e no processo de reflexão sobre a forma como concebemos a nova arquitetura do grupo Milvus.

Se estiver interessado nos pormenores de implementação dos componentes de grupo do Milvus, não deixe de consultar a documentação do Milvus. Iremos publicar continuamente artigos técnicos no repositório Milvus GitHub, no sítio Web Milvus e no Blogue Milvus.

O cluster de base de dados ideal

"Apontar pequeno, falhar pequeno."

Comecemos por enumerar as capacidades críticas que um cluster de bases de dados ideal deve ter.

  1. Concorrência e ausência de um ponto único de falha: os utilizadores ligados a diferentes membros do grupo podem ter acesso simultâneo de leitura/escrita à mesma parte dos dados.
  2. Consistência: diferentes membros do grupo devem ver os mesmos dados.
  3. Escalabilidade: podemos adicionar ou remover membros do grupo em qualquer altura.

Honestamente, todas estas capacidades são difíceis de adquirir em conjunto. Nas implementações modernas de clusters de bases de dados, as pessoas têm de comprometer algumas destas capacidades. As pessoas não esperam um cluster de bases de dados perfeito, desde que este se adapte aos cenários dos utilizadores. No entanto, o cluster "tudo partilhado" já esteve muito próximo de um cluster de bases de dados ideal. Se quisermos aprender alguma coisa, devemos começar por aqui.

As principais considerações de um cluster de bases de dados

O cluster partilhado tem uma história mais longa em comparação com outras implementações modernas. O grupo de partilha de dados Db2 e o Oracle RAC são típicos dos clusters de partilha total. Muitas pessoas pensam que tudo partilhado significa partilhar discos. É muito mais do que isso.

Um cluster de partilha de tudo só tem um tipo de membro de base de dados no grupo. Os utilizadores podem ligar-se a qualquer um destes membros simétricos para aceder a quaisquer dados. O que é "tudo" que precisa de ser partilhado para que isto funcione?

A sequência de eventos no grupo

Em primeiro lugar, a sequência de eventos do grupo é crucial para resolver os potenciais conflitos causados pelo acesso simultâneo de diferentes membros do grupo. Normalmente, utilizamos o número de sequência do registo de registo da base de dados para representar a sequência de eventos. Ao mesmo tempo, o número de sequência do registo de log é geralmente gerado a partir do carimbo de data/hora.

Assim, a necessidade de uma sequência de eventos de grupo é igual à necessidade de um temporizador global. Se pudéssemos ter um relógio atómico para o grupo, isso seria fabuloso. No entanto, o Milvus é um projeto de software de código aberto, o que significa que devemos confiar em recursos comuns disponíveis. Até à data, um relógio atómico continua a ser uma opção de luxo para as grandes empresas.

Implementámos a componente de sincronização de tempo no cluster da base de dados Milvus 2.0. Pode encontrar a ligação no apêndice.

Bloqueio global

A base de dados tem um mecanismo de bloqueio para resolver conflitos de acesso simultâneos, quer se trate de bloqueios optimistas ou pessimistas. Do mesmo modo, precisamos de um bloqueio global para resolver conflitos de acesso simultâneos entre diferentes membros do grupo.

O bloqueio global significa que os diferentes membros do grupo têm de falar uns com os outros para negociar os pedidos de bloqueio. Vários factores vitais podem afetar a eficiência deste processo de negociação do bloqueio global:

  • A velocidade das ligações inter-sistemas
  • O número de membros do grupo que precisam de participar no processo de negociação
  • A frequência dos conflitos de grupo

O tamanho típico do grupo não é superior a 100. Por exemplo, o Db2 DSG é 32; o Oracle RAC é 100. Esses membros do grupo serão colocados numa sala de servidores ligada por fibra ótica para minimizar a latência da transferência. É por isso que, por vezes, é designado por cluster centralizado. Devido à limitação da dimensão do grupo, as pessoas escolhem servidores de topo de gama (mainframes ou minicomputadores, que têm muito mais capacidade em termos de CPU, memória, canais de E/S, etc.) para constituírem os clusters de tudo partilhado.

Esta presunção de hardware mudou drasticamente no ambiente de nuvem moderno. Atualmente, os centros de dados na nuvem são constituídos por salas de servidores muito densas, cheias de (milhares de) servidores X86 de base com ligações TCP/IP. Se nos basearmos nestes servidores X86 para construir o cluster da base de dados, a dimensão do grupo deverá aumentar para centenas de (até milhares de) máquinas. E, nalguns cenários empresariais, queremos que estas centenas de máquinas X86 se espalhem por diferentes regiões. Assim, a implementação do bloqueio global pode já não valer a pena, uma vez que o desempenho do bloqueio global não será suficientemente bom.

No Milvus 2.0, não vamos implementar o recurso de bloqueio global. Por um lado, não há atualização para dados vectoriais. (As pessoas preferem apagar-então-inserir em vez de atualizar.) Por isso, não precisamos de nos preocupar com os conflitos entre vários escritores sobre o mesmo pedaço de dados no grupo Milvus com disposição em sharding. Entretanto, podemos utilizar o MVCC (controlo de simultaneidade multi-versão, um método de controlo de simultaneidade que evita bloqueios) para resolver os conflitos entre leitores e escritores.

Por outro lado, o processamento de dados vectoriais consome muito mais memória do que o processamento de dados estruturados. As pessoas procuram uma escalabilidade muito maior nas bases de dados vectoriais.

Cache de dados partilhada na memória

Podemos dividir brevemente um motor de base de dados em duas partes: o motor de armazenamento e o motor de computação. O motor de armazenamento é responsável por duas tarefas críticas:

  • Escrever dados no armazenamento permanente para efeitos de durabilidade.
  • Carregar dados do armazenamento permanente para a cache de dados na memória (também conhecida como buffer pool); este é o único local onde o motor de processamento acede aos dados.

No cenário do cluster de bases de dados, e se o membro A tiver atualizado os dados armazenados em cache no membro B? Como é que o membro B pode saber que os seus dados em memória expiraram? O cluster clássico de tudo partilhado tem um mecanismo de invalidação cruzada do buffer para resolver este problema. O mecanismo de invalidação cruzada do buffer funcionará de forma semelhante ao bloqueio global se mantivermos uma forte consistência entre os membros do grupo. Como dito anteriormente, isso não é prático no ambiente de nuvem moderno. Por isso, decidimos baixar o nível de consistência no grupo Milvus escalável na nuvem para uma consistência eventual. Desta forma, o mecanismo de invalidação cruzada de buffer no Milvus 2.0 pode ser um processo assíncrono.

Armazenamento partilhado

O armazenamento partilhado é provavelmente a primeira coisa em que se pensa quando se fala de um cluster de bases de dados.

As opções de armazenamento também mudaram significativamente nos últimos anos de evolução do armazenamento em nuvem. A rede ligada ao armazenamento (SAN) era (e ainda é) a base de armazenamento do grupo de tudo partilhado. Mas no ambiente de nuvem, não há SAN. A base de dados tem de utilizar o disco local ligado às máquinas virtuais na nuvem. O uso do disco local introduz o desafio da consistência de dados entre os membros do grupo. E também temos de nos preocupar com a alta disponibilidade dos membros do grupo.

Em seguida, o Snowflake criou um excelente modelo para bases de dados na nuvem utilizando o armazenamento partilhado na nuvem (armazenamento S3). Também inspira o Milvus 2.0. Como já foi referido, tencionamos confiar numa infraestrutura de nuvem madura. Mas antes de podermos utilizar o armazenamento partilhado na nuvem, temos de pensar em algumas coisas.

Em primeiro lugar, o armazenamento S3 é barato e fiável, mas não foi concebido para acesso R/W instantâneo como nos cenários de bases de dados. Precisamos de criar os componentes de dados (a que chamamos nós de dados no Milvus 2.0) para fazer a ponte entre a memória/disco local e o armazenamento S3. Existem alguns exemplos (como Alluxio, JuiceFS, etc.) que podemos aprender. A razão pela qual não podemos integrar estes projectos diretamente é porque nos concentramos em granularidades de dados diferentes. O Alluxio e o JuiceFS foram concebidos para conjuntos de dados ou ficheiros POSIX, enquanto nós nos concentramos no nível do registo de dados (vetor).

Quando os dados vectoriais são instalados no armazenamento S3, a resposta para os metadados é fácil: armazená-los no ETCD. E quanto aos dados de registo? Nas implementações clássicas, o armazenamento de registos também se baseia no SAN. Os ficheiros de registo de um membro do grupo de bases de dados são partilhados no cluster de bases de dados para efeitos de recuperação de falhas. Portanto, isto não era um problema até entrarmos no ambiente de nuvem.

No artigo sobre o Spanner, a Google ilustrou a forma como implementou a base de dados (grupo) distribuída globalmente com o algoritmo de consenso Paxos. É necessário programar o cluster da base de dados como um grupo de replicação de máquinas de estado. O redo log é normalmente o "estado" que será replicado no grupo.

A replicação do redo-log através de algoritmos de consenso é uma ferramenta poderosa e apresenta vantagens substanciais em alguns cenários empresariais. Mas para a base de dados vetorial Milvus, não encontramos incentivos suficientes para criar um grupo de replicação de máquinas de estado como um todo. Decidimos utilizar a fila/plataforma de mensagens na nuvem (Apache Pulsar, Apache Kafka, etc.) como uma alternativa de armazenamento partilhado na nuvem para o armazenamento de registos. Ao delegar o armazenamento de registos na plataforma de mensagens, obtemos os benefícios abaixo.

  • O grupo é mais orientado para os eventos, o que significa que muitos processos podem ser assíncronos. Isso melhora a escalabilidade.
  • Os componentes são mais fracamente acoplados, o que facilita muito a realização de actualizações contínuas em linha. Melhora a disponibilidade e a operacionalidade.

Voltaremos a abordar este tópico na secção seguinte.

Até agora, resumimos as considerações cruciais do cluster da base de dados. Antes de passarmos à discussão sobre a arquitetura do Milvus 2.0, deixe-me explicar primeiro como gerimos os vectores no Milvus.

Gestão de dados e previsibilidade do desempenho

O Milvus armazena os vectores em colecções. A "coleção" é um conceito lógico, equivalente a uma "tabela" nas bases de dados SQL. Uma "coleção" pode ter vários ficheiros físicos para guardar os vectores. Um ficheiro físico é um "segmento". O "segmento" é um conceito físico como um ficheiro de espaço de tabela nas bases de dados SQL. Quando o volume de dados é pequeno, podemos guardar tudo num único segmento/ficheiro físico. Mas hoje em dia, estamos constantemente a enfrentar grandes volumes de dados. Quando existem vários segmentos/ficheiros físicos, como devemos distribuir os dados por diferentes partições de dados?

Embora os dados estejam em primeiro lugar e não os índices, temos de armazenar os dados da forma que o algoritmo do índice prefere para que o acesso aos dados seja eficiente na maioria dos casos. Uma estratégia frequentemente utilizada nas bases de dados SQL é a partição pelo intervalo de valores da chave de partição. Normalmente, as pessoas criam um índice agrupado para aplicar a chave de partição. De um modo geral, esta é uma boa abordagem para as bases de dados SQL. Os dados são armazenados em bom estado, optimizados para E/S (pré-busca). Mas ainda existem defeitos.

  • Inclinação dos dados. Algumas das partições podem ter muito mais dados do que outras. A distribuição dos dados do mundo real não é tão simples como o intervalo numérico.
  • Hotspots de acesso. Pode haver mais carga de trabalho nalgumas partições de dados.

Imagine que mais carga de trabalho vai para as partições com mais dados. Precisamos de reequilibrar os dados entre as partições quando estas situações ocorrem. (Este é o tedioso dia a dia de um DBA).

The Clustered index for vectors O índice clusterizado para vetores

Também podemos criar um índice agrupado para vectores (um índice de lista invertida). Mas este não é o mesmo caso das bases de dados SQL. Uma vez criado o índice nas bases de dados SQL, é muito eficiente aceder aos dados através do índice, com menos computação e menos operações de E/S. Mas para dados vectoriais, haverá muito mais computação e operações de E/S, mesmo com um índice. Assim, os defeitos mencionados anteriormente terão um impacto mais grave nos clusters de bases de dados vectoriais. Além disso, o custo de reequilibrar os vectores em diferentes segmentos é muito elevado devido ao volume de dados e à complexidade da computação.

Em Milvus, utilizamos a estratégia de partição por crescimento. Quando injectamos dados numa coleção de vectores, Milvus anexa os novos vectores ao último segmento da coleção. Milvus fecha o segmento quando o seu tamanho é suficientemente grande (o limite é configurável) e constrói o índice para o segmento fechado. Entretanto, será criado um novo segmento para armazenar os próximos dados. Esta estratégia simples é mais equilibrada para o processamento vetorial.

A consulta vetorial é um processo de pesquisa dos candidatos mais semelhantes na coleção vetorial. Trata-se de um procedimento típico do MapReduce. Por exemplo, queremos procurar os 20 melhores resultados semelhantes de uma coleção de vectores com dez segmentos. Podemos procurar os 20 melhores em cada um dos segmentos e, em seguida, fundir os 20 * 10 resultados nos 20 resultados finais. Uma vez que cada segmento tem a mesma quantidade de vectores e um índice semelhante, o tempo de processamento em cada segmento é quase idêntico. Isto dá-nos a vantagem da previsibilidade do desempenho, que é essencial quando se planeia a escala dos clusters da base de dados.

Novos paradigmas no Milvus 2.0

No Milvus 1.0, implementámos um grupo de sharding de divisão de leitura/escrita como a maioria das bases de dados SQL. Foi uma boa tentativa de escalar o cluster da base de dados Milvus. Mas os problemas também são bastante óbvios.

Milvus database 1.0 Base de dados Milvus 1.0

No Milvus 1.0, o nó R/W tem de tomar conta do segmento mais recente, incluindo a anexação de vectores, a pesquisa neste segmento não indexado, a construção do índice, etc. Uma vez que cada coleção só tem um escritor, o escritor está muito ocupado se os dados forem continuamente transmitidos para o sistema. O desempenho da partilha de dados entre o nó R/W e os nós leitores é também um problema. Além disso, temos de confiar no NFS (não é estável) ou no armazenamento em nuvem premium (demasiado caro) para o armazenamento de dados partilhados.

Estes problemas existentes são difíceis de resolver na arquitetura Milvus 1.0. Assim, introduzimos novos paradigmas na conceção do Milvus 2.0 para resolver estas questões.

Milvus architecture Arquitetura Milvus

Modelo de ator

Existem dois modelos para programar sistemas de computação concorrentes.

  • A memória partilhada, que implica o controlo da concorrência (bloqueio) e o processamento síncrono
  • O modelo de ator (também conhecido por passagem de mensagens) significa processamento assíncrono e orientado para as mensagens

Também podemos aplicar estes dois modelos em clusters de bases de dados distribuídas.

Como já foi referido, a maioria das bases de dados distribuídas de alto nível utiliza o mesmo método: replicação de redo-log através de algoritmos de consenso. Trata-se de um processamento síncrono que utiliza algoritmos de consenso para criar uma memória partilhada distribuída para os registos redo-log. Diferentes empresas e capitais de risco investiram milhares de milhões de dólares nesta tecnologia. Não quis comentar este assunto até começarmos a trabalhar no Milvus 2.0. Muitas pessoas consideram esta tecnologia como a única forma de realizar sistemas de bases de dados distribuídas. Isto é irritante. Se eu não disser nada, as pessoas podem interpretar mal o facto de termos sido imprudentes na conceção de bases de dados distribuídas.

Nos últimos anos, a replicação do Redo-log através de algoritmos de consenso tem sido a tecnologia de bases de dados mais sobrevalorizada. Há duas questões fundamentais.

  • A presunção de que a replicação de redo-logs é melhor é frágil.
  • Os vendedores induzem em erro as expectativas das pessoas quanto à capacidade dos algoritmos de consenso.

Digamos que temos dois nós de base de dados, o nó de origem e o nó de destino. No início, ambos têm a cópia exacta dos dados. Temos algumas operações de alteração (instruções SQL I/U/D) no nó de origem e queremos manter o nó de destino atualizado. O que é que devemos fazer? A forma mais simples é repetir as operações no nó de destino. Mas esta não é a forma mais eficiente.

Pensando no custo de execução de uma instrução I/U/D, podemos dividi-lo nas partes de preparação da execução e de trabalho físico. A parte de preparação da execução inclui o trabalho do analisador SQL, do optimizador SQL, etc. Independentemente do número de registos de dados que serão afectados, este é um custo fixo. O custo da parte do trabalho físico depende do número de registos de dados que serão afectados; é um custo flutuante. A ideia subjacente à replicação do redo-log é poupar o custo fixo no nó de destino; apenas reproduzimos o redo-log (o trabalho físico) no nó de destino.

A percentagem de poupança de custos é o recíproco do número de registos redo-log. Se uma operação afetar apenas um registo, deverei obter poupanças significativas com a replicação do redo-log. E se forem 10.000 registos? Então devemos preocupar-nos com a fiabilidade da rede. Qual é mais fiável, enviar a única operação ou os 10.000 registos de redo-log? E se for um milhão de registos? A replicação de redo-log é excelente em cenários como sistemas de pagamento, sistemas de metadados, etc. Nestes cenários, cada operação I/U/D da base de dados afecta apenas um pequeno número de registos (1 ou 2). Mas é difícil trabalhar com cargas de trabalho de E/S intensivas, como trabalhos em lote.

Os fornecedores afirmam sempre que os algoritmos de consenso podem proporcionar uma forte consistência aos clusters de bases de dados. Mas as pessoas só utilizam algoritmos de consenso para replicar os registos de redo-log. Os registos redo-log são consistentes em nós diferentes, mas isso não significa que as visualizações de dados noutros nós também o sejam. Temos de fundir os registos de redo-log com os registos reais da tabela. Portanto, mesmo com esse processamento síncrono, ainda só podemos obter consistência eventual nas exibições de dados.

Devemos utilizar a replicação do redo-log através de algoritmos de consenso nos locais apropriados. O sistema de metadados (ETCD) e a plataforma de mensagens (e.g., Apache Pulsar) usados no Milvus 2.0 implementaram algoritmos de consenso. Mas, como disse antes, "para a base de dados vetorial Milvus, não encontramos incentivos suficientes para ser um grupo de replicação de máquinas de estado como um todo".

No Milvus 2.0, usamos o modelo de ator para organizar os nós de trabalho. Os nós de trabalho são solitários. Eles só falam com a plataforma de mensagens, recebendo comandos e enviando resultados. Parece aborrecido.

"Qual é o nosso lema?" "O aborrecido é sempre o melhor." - O Guarda-Costas de Hitman (2017)

O modelo de ator é assíncrono. É adequado para escalabilidade e disponibilidade. Uma vez que os nós de trabalho não se conhecem uns aos outros, não há impacto noutros nós de trabalho se alguns dos nós de trabalho se juntarem ou forem removidos.

Separação da disponibilidade e da durabilidade

No Milvus 2.0, fazemos a repetição de operações em vez da repetição de registos, porque na base de dados vetorial não há grande diferença entre a repetição de operações e a repetição de registos. Não temos a função Update nem a função Insert with Select. E também é muito mais fácil fazer a repetição de operação com o modelo de ator.

Assim, vários nós de trabalho podem executar a mesma operação a partir da plataforma de mensagens de acordo com a sua responsabilidade. Já referi que decidimos utilizar o armazenamento em nuvem S3 como camada de armazenamento partilhado do cluster da base de dados Milvus. O armazenamento S3 é muito fiável. Então será necessário que diferentes nós de trabalho escrevam os mesmos dados no armazenamento partilhado?

Assim, concebemos três funções para os nós de trabalho.

  • O nó de consulta mantém uma vista de dados na memória de acordo com a atribuição. O trabalho do nó de consulta inclui fazer pesquisa vetorial e manter os dados na memória actualizados. Mas não precisa de escrever nada no armazenamento S3. É o nó mais sensível à memória do grupo.
  • O nó de dados é responsável por escrever os novos dados no armazenamento S3. O nó de dados não precisa de manter a vista de dados na memória, pelo que a configuração de hardware do nó de dados é bastante diferente da do nó de consulta.
  • O nó de índice constrói índices para os segmentos fechados pelo nó de dados quando o tamanho dos segmentos atinge o limite. Esse é o trabalho mais intensivo de CPU no grupo.

Esses três tipos de nós representam diferentes tipos de carga de trabalho. Eles podem escalar de forma independente. Chamamos-lhe separação de disponibilidade e durabilidade, aprendida com a base de dados em nuvem Microsoft Socrates.

O fim, também o princípio

Este artigo analisou várias decisões de conceção da base de dados vetorial Milvus 2.0. Vamos resumir rapidamente esses pontos aqui.

  • Escolhemos a consistência eventual para o cluster Milvus 2.0.
  • Integrámos, tanto quanto possível, os componentes maduros da nuvem no Milvus 2.0. Controlámos os novos componentes introduzidos pelo Milvus 2.0 nos ambientes de produção dos utilizadores.
  • Seguindo o modelo de ator e a separação entre disponibilidade e durabilidade, o Milvus 2.0 é fácil de escalar no ambiente de nuvem.

Até agora, formámos a espinha dorsal da base de dados escalável na nuvem do Milvus 2.0, mas o nosso atraso contém muitos requisitos da comunidade Milvus que precisam de ser satisfeitos. Se tem a mesma missão ("Construir mais software de infra-estruturas de código aberto para acelerar a transformação da IA"), seja bem-vindo a juntar-se à comunidade Milvus.

Milvus é um projeto de graduação da fundação LF AI & Data. NÃO precisa de assinar qualquer CLA para Milvus!

Apêndice

Documento de conceção do Milvus

https://github.com/milvus-io/milvus/tree/master/docs/design_docs

Implementação do Raft em C++

Se ainda estiver interessado no algoritmo de consenso, sugiro que consulte o projeto de código aberto Gringofts do eBay. Trata-se de uma implementação em C++ do algoritmo de consenso Raft (uma variante da família Paxos). O meu amigo Jacky e o Elvis (meus ex-colegas na Morgan Stanley) construíram-no para o sistema de pagamentos em linha do eBay, que é precisamente um dos cenários mais adequados para esta tecnologia.

Try Managed Milvus for Free

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

Get Started

Like the article? Spread the word

Continue Lendo