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

milvus-logo
LFAI

Preparação

  • Engineering
April 13, 2020
milvus

Neste artigo, iremos descrever principalmente a forma como os dados vectoriais são registados na memória do Milvus, e como estes registos são mantidos.

Abaixo estão os nossos principais objectivos de conceção:

  1. A eficiência da importação de dados deve ser elevada.
  2. Os dados podem ser vistos o mais rapidamente possível após a sua importação.
  3. Evitar a fragmentação dos ficheiros de dados.

Por conseguinte, criámos uma memória intermédia (memória intermédia de inserção) para inserir os dados, a fim de reduzir o número de comutações de contexto de E/S aleatórias no disco e no sistema operativo para melhorar o desempenho da inserção de dados. A arquitetura de armazenamento em memória baseada em MemTable e MemTableFile permite-nos gerir e serializar os dados de forma mais conveniente. O estado do buffer é dividido em Mutável e Imutável, o que permite que os dados sejam persistidos no disco, mantendo os serviços externos disponíveis.

Preparação

Quando o utilizador está pronto para inserir um vetor no Milvus, precisa primeiro de criar uma Collection (* O Milvus renomeia Table para Collection na versão 0.7.0). A coleção é a unidade mais básica para registar e pesquisar vectores no Milvus.

Cada coleção tem um nome único e algumas propriedades que podem ser definidas, e os vectores são inseridos ou pesquisados com base no nome da coleção. Ao criar uma nova coleção, o Milvus regista a informação dessa coleção nos metadados.

Inserção de dados

Quando o utilizador envia um pedido de inserção de dados, os dados são serializados e desserializados para chegar ao servidor Milvus. Os dados são agora escritos em memória. A escrita em memória divide-se, grosso modo, nas seguintes etapas:

2-data-insertion-milvus.png 2-data-insertion-milvus.png

  1. No MemManager, localize ou crie uma nova MemTable correspondente ao nome da coleção. Cada MemTable corresponde a um buffer da Coleção na memória.
  2. Uma MemTable conterá um ou mais MemTableFile. Sempre que criamos um novo MemTableFile, registamos essa informação no Meta ao mesmo tempo. Dividimos os MemTableFile em dois estados: Mutável e Imutável. Quando o tamanho do MemTableFile atinge o limite, torna-se Imutável. Cada MemTable só pode ter um MemTableFile Mutável a ser escrito em qualquer altura.
  3. Os dados de cada MemTableFile serão finalmente registados na memória no formato do tipo de índice definido. A MemTableFile é a unidade mais básica para gerir dados em memória.
  4. Em qualquer altura, a utilização de memória dos dados inseridos não excederá o valor predefinido (insert_buffer_size). Isto porque, a cada pedido de inserção de dados, o MemManager pode facilmente calcular a memória ocupada pelo MemTableFile contido em cada MemTable e, em seguida, coordenar o pedido de inserção de acordo com a memória atual.

Através da arquitetura multi-nível do MemManager, MemTable e MemTableFile, a inserção de dados pode ser melhor gerida e mantida. Naturalmente, eles podem fazer muito mais do que isso.

Consultas quase em tempo real

No Milvus, só é necessário esperar um segundo, no máximo, para que os dados inseridos passem da memória para o disco. Todo esse processo pode ser resumido na figura a seguir:

2-near-real-time-query-milvus.png 2-near-real-time-query-milvus.png

Primeiro, os dados inseridos entrarão num buffer de inserção na memória. O buffer mudará periodicamente do estado Mutável inicial para o estado Imutável em preparação para a serialização. Em seguida, esses buffers imutáveis serão serializados para o disco periodicamente pelo thread de serialização em segundo plano. Depois de os dados serem colocados, a informação da ordem será registada nos metadados. Neste ponto, os dados podem ser pesquisados!

Agora, vamos descrever os passos da figura em pormenor.

Já conhecemos o processo de inserção de dados no buffer mutável. O próximo passo é mudar do buffer mutável para o buffer imutável:

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

A fila imutável fornecerá à thread de serialização em segundo plano o estado imutável e o MemTableFile que está pronto para ser serializado. Cada MemTable gerencia sua própria fila imutável, e quando o tamanho do único MemTableFile mutável da MemTable atinge o limite, ele entra na fila imutável. Uma thread em segundo plano responsável por ToImmutable irá periodicamente puxar todos os MemTableFiles na fila imutável gerenciada por MemTable e enviá-los para a fila total Immutable. É de notar que as duas operações de escrita de dados na memória e de alteração dos dados na memória para um estado que não pode ser escrito não podem ocorrer ao mesmo tempo, sendo necessário um bloqueio comum. No entanto, a operação de ToImmutable é muito simples e quase não causa qualquer atraso, pelo que o impacto no desempenho dos dados inseridos é mínimo.

O próximo passo é serializar o MemTableFile na fila de serialização para o disco. Isso é dividido principalmente em três etapas:

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

Primeiro, a thread de serialização em segundo plano puxa periodicamente o MemTableFile da fila imutável. Em seguida, eles são serializados em arquivos brutos de tamanho fixo (Raw TableFiles). Por fim, registamos esta informação nos metadados. Quando efectuarmos uma pesquisa vetorial, consultaremos o TableFile correspondente nos metadados. A partir daqui, estes dados podem ser pesquisados!

Além disso, de acordo com o conjunto index_file_size, depois de o thread de serialização completar um ciclo de serialização, irá fundir alguns TableFiles de tamanho fixo num TableFile e também registar estas informações nos metadados. Neste momento, o TableFile pode ser indexado. A construção do índice também é assíncrona. Outro thread em segundo plano responsável pela construção do índice lerá periodicamente o TableFile no estado ToIndex dos metadados para executar a construção do índice correspondente.

De facto, verificará que, com a ajuda do TableFile e dos metadados, a pesquisa vetorial se torna mais intuitiva e conveniente. Em geral, precisamos de obter os TableFiles correspondentes à coleção consultada a partir dos metadados, pesquisar em cada TableFile e, por fim, fundir. Neste artigo, não nos aprofundamos na implementação específica da pesquisa.

Se quiser saber mais, pode ler o nosso código fonte, ou ler os nossos outros artigos técnicos sobre o 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

Continue Lendo