Como é feita a gestão de dados em Milvus
Autor: Yihua Mo
Data: 2019-11-08
Como é feita a gestão de dados em Milvus
Antes de mais, alguns conceitos básicos de Milvus:
- Tabela: A tabela é um conjunto de dados de vectores, em que cada vetor tem um ID único. Cada vetor e o seu ID representam uma linha da tabela. Todos os vectores de uma tabela têm de ter as mesmas dimensões. Abaixo está um exemplo de uma tabela com vectores de 10 dimensões:
tabela
- Índice: A construção de um índice é o processo de agrupamento de vectores através de um determinado algoritmo, o que requer espaço adicional em disco. Alguns tipos de índices requerem menos espaço, uma vez que simplificam e comprimem os vectores, enquanto outros tipos requerem mais espaço do que os vectores em bruto.
No Milvus, os utilizadores podem executar tarefas como criar uma tabela, inserir vectores, criar índices, pesquisar vectores, obter informações da tabela, eliminar tabelas, remover dados parciais de uma tabela e remover índices, etc.
Suponhamos que temos 100 milhões de vectores de 512 dimensões e que é necessário inseri-los e geri-los no Milvus para uma pesquisa vetorial eficiente.
(1) Inserção de vectores
Vejamos como os vectores são inseridos no Milvus.
Como cada vetor ocupa 2 KB de espaço, o espaço mínimo de armazenamento para 100 milhões de vectores é de cerca de 200 GB, o que torna irrealista a inserção única de todos estes vectores. É necessário criar vários ficheiros de dados em vez de um. O desempenho da inserção é um dos principais indicadores de desempenho. O Milvus suporta a inserção única de centenas ou mesmo dezenas de milhares de vectores. Por exemplo, a inserção única de 30 mil vectores de 512 dimensões demora geralmente apenas 1 segundo.
inserção
Nem todas as inserções de vectores são carregadas no disco. Milvus reserva um buffer mutável na memória da CPU para cada tabela que é criada, onde os dados inseridos podem ser rapidamente escritos. E quando os dados no buffer mutável atingirem um determinado tamanho, este espaço será rotulado como imutável. Nesse meio tempo, um novo buffer mutável será reservado. Os dados no buffer imutável são gravados no disco regularmente e a memória correspondente da CPU é liberada. O mecanismo de gravação regular no disco é semelhante ao usado no Elasticsearch, que grava dados armazenados em buffer no disco a cada 1 segundo. Além disso, os utilizadores que estão familiarizados com o LevelDB/RocksDB podem ver aqui alguma semelhança com o MemTable.
Os objectivos do mecanismo de inserção de dados são:
- A inserção de dados deve ser eficiente.
- Os dados inseridos podem ser utilizados instantaneamente.
- Os ficheiros de dados não devem ser demasiado fragmentados.
(2) Ficheiro de dados brutos
Quando os vectores são escritos no disco, são guardados num ficheiro de dados brutos que contém os vectores brutos. Como mencionado anteriormente, os vectores de grande escala têm de ser guardados e geridos em vários ficheiros de dados. O tamanho dos dados inseridos varia, uma vez que os utilizadores podem inserir 10 vectores ou 1 milhão de vectores de uma só vez. No entanto, a operação de escrita no disco é executada uma vez a cada 1 segundo. Assim, são gerados ficheiros de dados de diferentes tamanhos.
Os ficheiros de dados fragmentados não são fáceis de gerir nem de aceder para a pesquisa de vectores. O Milvus funde constantemente estes pequenos ficheiros de dados até que o tamanho do ficheiro fundido atinja um determinado tamanho, por exemplo, 1GB. Este tamanho específico pode ser configurado no parâmetro da API index_file_size
na criação de tabelas. Assim, 100 milhões de vectores de 512 dimensões serão distribuídos e guardados em cerca de 200 ficheiros de dados.
Tendo em conta os cenários de computação incremental, em que os vectores são inseridos e pesquisados em simultâneo, temos de nos certificar de que, assim que os vectores são escritos no disco, estão disponíveis para pesquisa. Assim, antes de os pequenos ficheiros de dados serem fundidos, podem ser acedidos e pesquisados. Quando a fusão estiver concluída, os ficheiros de dados pequenos serão removidos e os ficheiros recentemente fundidos serão utilizados para pesquisa.
Este é o aspeto dos ficheiros consultados antes da fusão:
rawdata1
Ficheiros consultados após a fusão:
rawdata2
(3) Ficheiro de índice
A pesquisa baseada no ficheiro de dados brutos é uma pesquisa de força bruta que compara as distâncias entre os vectores de consulta e os vectores de origem e calcula os k vectores mais próximos. A pesquisa por força bruta é ineficiente. A eficiência da pesquisa pode ser grandemente aumentada se a pesquisa for baseada no Ficheiro de índice onde os vectores são indexados. A criação de um índice requer espaço adicional em disco e é normalmente demorada.
Então, quais são as diferenças entre ficheiros de dados brutos e ficheiros de índice? Simplificando, o ficheiro de dados brutos regista cada vetor juntamente com o seu ID único, enquanto o ficheiro de índice regista os resultados do agrupamento de vectores, como o tipo de índice, os centróides do agrupamento e os vectores em cada agrupamento.
Ficheiro de índice
De um modo geral, o Ficheiro de índice contém mais informações do que o Ficheiro de dados brutos, mas os tamanhos dos ficheiros são muito mais pequenos, uma vez que os vectores são simplificados e quantificados durante o processo de construção do índice (para determinados tipos de índices).
As tabelas recém-criadas são, por defeito, pesquisadas por computação bruta. Assim que o índice é criado no sistema, o Milvus constrói automaticamente o índice para ficheiros fundidos que atinjam o tamanho de 1 GB numa thread autónoma. Quando a construção do índice estiver concluída, é gerado um novo ficheiro de índice. Os ficheiros de dados brutos serão arquivados para a construção de índices com base noutros tipos de índices.
O Milvus constrói automaticamente o índice para os ficheiros que atingem 1 GB:
buildindex
Construção de índice concluída:
indexcomplete
O índice não será criado automaticamente para ficheiros de dados brutos que não atinjam 1 GB, o que pode diminuir a velocidade de pesquisa. Para evitar esta situação, é necessário forçar manualmente a construção do índice para esta tabela.
forcebuild
Depois de forçar a criação do índice para o ficheiro, o desempenho da pesquisa é bastante melhorado.
indexfinal
(4) Meta dados
Como mencionado anteriormente, 100 milhões de vectores de 512 dimensões são guardados em 200 ficheiros de disco. Quando o índice é construído para estes vectores, haverá 200 ficheiros de índice adicionais, o que faz com que o número total de ficheiros seja 400 (incluindo os ficheiros de disco e os ficheiros de índice). É necessário um mecanismo eficiente para gerir os metadados (estado dos ficheiros e outras informações) destes ficheiros, a fim de verificar o seu estado, remover ou criar ficheiros.
A utilização de bases de dados OLTP para gerir estas informações é uma boa opção. O Milvus autónomo utiliza o SQLite para gerir os metadados, enquanto que na implementação distribuída, o Milvus utiliza o MySQL. Quando o servidor Milvus arranca, são criadas 2 tabelas (nomeadamente 'Tables' e 'TableFiles') em SQLite/MySQL, respetivamente. 'Tables' regista a informação das tabelas e 'TableFiles' regista a informação dos ficheiros de dados e dos ficheiros de índice.
Como demonstrado no fluxograma abaixo, 'Tables' contém metadados como o nome da tabela (table_id), a dimensão do vetor (dimension), a data de criação da tabela (created_on), o estado da tabela (state), o tipo de índice (engine_type), o número de clusters do vetor (nlist) e o método de cálculo da distância (metric_type).
E 'TableFiles' contém o nome da tabela a que o ficheiro pertence (table_id), o tipo de índice do ficheiro (engine_type), o nome do ficheiro (file_id), o tipo de ficheiro (file_type), o tamanho do ficheiro (file_size), o número de linhas (row_count) e a data de criação do ficheiro (created_on).
metadados
Com estes metadados, podem ser executadas várias operações. Seguem-se alguns exemplos:
- Para criar uma tabela, o Meta Manager só precisa de executar uma instrução SQL:
INSERT INTO TABLES VALUES(1, 'table_2, 512, xxx, xxx, ...)
. - Para executar a pesquisa vetorial na tabela_2, o MetaManager executará uma consulta em SQLite/MySQL, que é uma instrução SQL de facto:
SELECT * FROM TableFiles WHERE table_id='table_2'
para obter as informações dos ficheiros da tabela_2. Em seguida, estes ficheiros serão carregados na memória pelo Query Scheduler para o cálculo da pesquisa. - Não é permitido eliminar instantaneamente uma tabela, uma vez que podem estar a ser executadas consultas nessa tabela. É por isso que existem as opções "soft-delete" e "hard-delete" para uma tabela. Quando se elimina uma tabela, esta é rotulada como "soft-delete" e não é permitido efetuar mais consultas ou alterações. No entanto, as consultas que estavam a ser executadas antes da eliminação continuam a ser executadas. Só quando todas estas consultas pré-exclusão estiverem concluídas, a tabela, juntamente com os seus metadados e ficheiros relacionados, será definitivamente eliminada.
(5) Programador de consultas
O gráfico abaixo demonstra o processo de pesquisa de vectores tanto na CPU como na GPU, consultando os ficheiros (ficheiros de dados brutos e ficheiros de índice) que são copiados e guardados no disco, na memória da CPU e na memória da GPU para os topk vectores mais semelhantes.
topkresultado
O algoritmo de programação de consultas melhora significativamente o desempenho do sistema. A filosofia básica de conceção consiste em obter o melhor desempenho de pesquisa através da utilização máxima dos recursos de hardware. Abaixo está apenas uma breve descrição do agendador de consultas e haverá um artigo dedicado a este tópico no futuro.
Chamamos à primeira consulta a uma dada tabela a consulta "fria" e às consultas subsequentes a consulta "quente". Quando a primeira consulta é feita contra uma determinada tabela, o Milvus faz muito trabalho para carregar dados na memória da CPU e alguns dados na memória da GPU, o que consome muito tempo. Em consultas posteriores, a pesquisa é muito mais rápida, pois parte ou todos os dados já estão na memória da CPU, o que economiza o tempo de leitura do disco.
Para reduzir o tempo de pesquisa da primeira consulta, o Milvus fornece a configuração Preload Table (preload_table
), que permite o pré-carregamento automático de tabelas na memória da CPU aquando do arranque do servidor. Para uma tabela que contenha 100 milhões de vectores de 512 dimensões, o que corresponde a 200 GB, a velocidade de pesquisa é mais rápida se houver memória suficiente na CPU para armazenar todos estes dados. No entanto, se a tabela contiver vectores de milhares de milhões de dimensões, é por vezes inevitável libertar memória da CPU/GPU para adicionar novos dados que não são consultados. Atualmente, utilizamos o LRU (Latest Recently Used) como estratégia de substituição de dados.
Como mostra o gráfico abaixo, suponha que existe uma tabela que tem 6 ficheiros de índice armazenados no disco. A memória da CPU só pode armazenar 3 ficheiros de índice e a memória da GPU apenas 1 ficheiro de índice.
Quando a pesquisa começa, 3 ficheiros de índice são carregados na memória da CPU para consulta. O primeiro ficheiro será libertado da memória da CPU imediatamente após ser consultado. Entretanto, o 4º ficheiro é carregado na memória da CPU. Da mesma forma, quando um ficheiro é consultado na memória da GPU, é imediatamente libertado e substituído por um novo ficheiro.
O programador de consultas lida principalmente com 2 conjuntos de filas de tarefas, uma fila é sobre o carregamento de dados e outra é sobre a execução da pesquisa.
Queryschedule
(6) Redutor de resultados
Há dois parâmetros-chave relacionados com a pesquisa vetorial: um é "n", que significa o número n de vectores-alvo; o outro é "k", que significa os k vectores mais semelhantes. Os resultados da pesquisa são, de facto, n conjuntos de KVP (pares chave-valor), cada um com k pares de chave-valor. Como as consultas têm de ser executadas em relação a cada ficheiro individual, independentemente de se tratar de um ficheiro de dados brutos ou de um ficheiro de índice, serão obtidos n conjuntos de resultados dos k melhores para cada ficheiro. Todos estes conjuntos de resultados são fundidos para obter os conjuntos de resultados top-k da tabela.
O exemplo abaixo mostra como os conjuntos de resultados são fundidos e reduzidos para a pesquisa vetorial numa tabela com 4 ficheiros de índice (n=2, k=3). Note-se que cada conjunto de resultados tem 2 colunas. A coluna da esquerda representa a identificação do vetor e a coluna da direita representa a distância euclidiana.
resultado
(7) Otimização futura
Seguem-se algumas reflexões sobre possíveis optimizações da gestão de dados.
- E se os dados no buffer imutável ou mesmo no buffer mutável também pudessem ser consultados instantaneamente? Atualmente, os dados na memória intermutável não podem ser consultados, não até serem escritos no disco. Alguns utilizadores estão mais interessados no acesso instantâneo aos dados após a inserção.
- Fornecer uma funcionalidade de particionamento de tabelas que permita ao utilizador dividir tabelas muito grandes em partições mais pequenas e executar uma pesquisa vetorial numa determinada partição.
- Adicionar aos vectores alguns atributos que possam ser filtrados. Por exemplo, alguns utilizadores só querem pesquisar entre os vectores com determinados atributos. É necessário recuperar os atributos dos vectores e mesmo os vectores brutos. Uma abordagem possível é utilizar uma base de dados KV, como a RocksDB.
- Fornecer uma funcionalidade de migração de dados que permita a migração automática de dados desactualizados para outro espaço de armazenamento. Em alguns cenários em que os dados fluem constantemente, os dados podem estar a envelhecer. Como alguns utilizadores apenas se preocupam com os dados do mês mais recente e executam pesquisas com base nos mesmos, os dados mais antigos tornam-se menos úteis e consomem muito espaço em disco. Um mecanismo de migração de dados ajuda a libertar espaço em disco para novos dados.
Resumo
Este artigo apresenta principalmente a estratégia de gestão de dados no Milvus. Em breve, serão publicados mais artigos sobre a implementação distribuída do Milvus, a seleção de métodos de indexação vetorial e o programador de consultas. Fique atento!
Blogues relacionados
- Resumo
- Blogues relacionados
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