Cada coleção de dados é dividida em segmentos. Cada segmento possui armazenamento de vetor independente, armazenamento de carga útil e índice.

Os dados nos segmentos geralmente não se sobrepõem. No entanto, armazenar o mesmo ponto em diferentes segmentos não causa problemas porque a pesquisa contém um mecanismo de deduplicação.

Os segmentos incluem armazenamento de vetor, armazenamento de carga útil, índice de vetor, índice de carga útil e um mapeador de ID para armazenar a relação entre IDs internos e externos.

Os segmentos podem ser "adicionáveis" ou "não-adicionáveis", dependendo do tipo de armazenamento e índice utilizados. Você pode adicionar, excluir e consultar dados livremente em segmentos "adicionáveis", enquanto os segmentos "não-adicionáveis" só podem ler e excluir dados.

A configuração de segmentos em uma coleção pode ser diferente e independente entre si, mas é necessário pelo menos um segmento "adicionável".

Armazenamento de Vetor

Dependendo das necessidades da aplicação, o Qdrant pode utilizar uma das seguintes opções de armazenamento de dados. A escolha deve equilibrar entre a velocidade de pesquisa e o uso de RAM.

Armazenamento em Memória - Armazena todos os vetores na RAM, proporcionando a maior velocidade, pois o acesso ao disco é apenas necessário durante a persistência.

Armazenamento em Memmap - Cria um espaço de endereço virtual associado a um arquivo no disco. Os arquivos mapeados não são carregados diretamente na RAM, mas acessados usando o cache de página. Esta abordagem permite o uso flexível da memória disponível. Com RAM suficiente, sua velocidade é quase tão rápida quanto o armazenamento em memória.

Configurando o Armazenamento em Memmap

Existem duas maneiras de configurar o uso do memmap (também conhecido como armazenamento em disco):

  • Definir a opção on_disk para vetores na API de criação de coleções:

Apenas aplicável a v1.2.0 e superior

PUT /collections/{collection_name}

{
    "vectors": {
      "size": 768,
      "distance": "Cosseno",
      "on_disk": true
    }
}

Isso criará uma coleção que armazena imediatamente todos os vetores no armazenamento em memmap. Esta é a abordagem recomendada quando a instância do Qdrant usa discos rápidos e precisa lidar com coleções grandes.

  • Definir a opção memmap_threshold_kb. Esta opção define o limite para converter segmentos em armazenamento em memmap.

Existem duas maneiras de implementar isso:

  1. Definir globalmente o limite no arquivo de configuração. O nome do parâmetro é memmap_threshold_kb.
  2. Definir o limite individualmente para cada coleção durante a criação ou atualização.
PUT /collections/{collection_name}

{
    "vectors": {
      "size": 768,
      "distance": "Cosseno"
    },
    "optimizers_config": {
        "memmap_threshold": 20000
    }
}

A regra geral para definir o parâmetro de limite de memmap é simples:

  • Se o cenário de uso for equilibrado, defina o limite de memmap igual ao indexing_threshold (padrão é 20000). Neste caso, o otimizador não executará execuções adicionais e otimizará todos os limites de uma vez.
  • Se a carga de gravação for alta e a RAM for baixa, defina o limite de memmap como inferior ao indexing_threshold, por exemplo, 10000. Neste caso, o otimizador converterá primeiro os segmentos em armazenamento em memmap e depois aplicará a indexação.

Além disso, você pode usar o armazenamento em memmap não apenas para vetores, mas também para índices HNSW. Para habilitar esse recurso, defina o parâmetro hnsw_config.on_disk como true ao criar a coleção.

PUT /collections/{collection_name}

{
    "vectors": {
      "size": 768,
      "distance": "Cosseno"
    },
    "optimizers_config": {
        "memmap_threshold": 20000
    },
    "hnsw_config": {
        "on_disk": true
    }
}

Armazenamento de Carga

O Qdrant suporta dois tipos de armazenamento de carga: Na Memória e em Disco.

O armazenamento de carga na memória organiza os dados da carga da mesma maneira que os vetores em memória. Os dados da carga são carregados na memória quando o serviço é iniciado, enquanto o disco e o RocksDB são usados apenas para persistência. Este tipo de armazenamento é muito rápido, mas pode requerer uma grande quantidade de espaço de memória para armazenar todos os dados, especialmente se a carga incluir valores grandes (como resumos de texto ou até mesmo imagens).

Para valores grandes de carga, é preferível utilizar o armazenamento de carga em disco. Este tipo de armazenamento lê e escreve a carga diretamente no RocksDB, não requerendo assim uma grande quantidade de memória para armazenamento. No entanto, a desvantagem é a latência de acesso. Se precisar consultar vetores com base em condições de carga, verificar os valores armazenados no disco pode levar muito tempo. Neste caso, recomendamos criar um índice de carga para cada campo usado para condições de filtragem, a fim de evitar o acesso ao disco. Uma vez que o índice do campo é criado, o Qdrant manterá sempre todos os valores do campo indexado na memória, independentemente do tipo de armazenamento de carga.

Você pode especificar o tipo de armazenamento de carga desejado através do arquivo de configuração ou usando o parâmetro de coleção on_disk_payload ao criar uma coleção.

Controle de Versão

Para garantir a integridade dos dados, o Qdrant realiza todas as alterações de dados em duas etapas. Primeiramente, os dados são escritos no log de pré-gravação (WAL), que classifica e atribui números sequenciais a todas as operações.

Uma vez que as alterações são adicionadas ao WAL, elas não são perdidas mesmo em caso de queda de energia. Em seguida, as alterações entram no segmento. Cada segmento armazena a versão mais recente das alterações aplicadas a ele, bem como a versão de cada ponto individual. Se o número sequencial de uma nova alteração for inferior à versão atual de um ponto, o atualizador ignorará essa alteração. Esse mecanismo permite que o Qdrant recupere eficientemente o armazenamento a partir do WAL em caso de desligamentos anormais.