Ajustar a performance da consulta de vetor

Selecione uma versão da documentação:

Este documento mostra como ajustar seus índices para alcançar um desempenho de consulta mais rápido e um recall melhor.

Ajustar um índice ScaNN

O índice ScaNN usa indexação baseada em quantização de árvore. Nas técnicas de quantização de árvore, os índices aprendem uma árvore de pesquisa junto com uma função de quantização (ou hash). Quando você executa uma consulta, a árvore de pesquisa é usada para reduzir o espaço de pesquisa, e a quantização é usada para compactar o tamanho do índice. Essa remoção acelera a pontuação da similaridade (ou seja, distância) entre o vetor de consulta e os vetores do banco de dados.

Para alcançar uma alta taxa de consultas por segundo (QPS) e um recall alto com suas consultas de vizinho mais próximo, particione a árvore do seu índice ScaNN da maneira mais adequada aos seus dados e consultas.

Antes de criar um índice ScaNN, faça o seguinte:

  • Verifique se uma tabela com seus dados já foi criada.
  • Verifique se o valor definido para a flag maintenance_work_mem e shared_buffers é menor que a memória total da máquina para evitar problemas ao gerar o índice.

Parâmetros de ajuste

Os parâmetros de índice e flags de banco de dados a seguir são usados juntos para encontrar o equilíbrio certo de recall e QPS. Todos os parâmetros se aplicam aos dois tipos de índice ScaNN.

Parâmetro de ajuste Descrição Tipo de parâmetro
num_leaves O número de partições a serem aplicadas a este índice. O número de partições aplicadas ao criar um índice afeta o desempenho dele. Ao aumentar as partições para um determinado número de vetores, você cria um índice mais refinado, o que melhora a recuperação e o desempenho da consulta. No entanto, isso aumenta o tempo de criação do índice.

Como as árvores de três níveis são criadas mais rápido do que as de dois níveis, é possível aumentar o num_leaves_value ao criar um índice de árvore de três níveis para melhorar o desempenho.
  • Índice de dois níveis: defina esse valor como qualquer valor entre 1 e 1048576.

    Se você não tiver certeza sobre o valor exato, use sqrt(ROWS) como ponto de partida, em que ROWS é o número de linhas de vetor. O número de vetores que cada partição contém é calculado por
    ROWS/sqrt(ROWS) = sqrt(ROWS).

    Como um índice de árvore de dois níveis pode ser criado em um conjunto de dados com menos de 10 milhões de linhas de vetores, cada partição terá menos de (sqrt(10M)) vetores, que são 3200 vetores. Para um desempenho ideal, recomendamos minimizar o número de vetores em cada partição.
  • Índice de três níveis: defina esse valor como qualquer valor entre 1 e 1048576.

    Se você não tiver certeza sobre o valor exato, use power(ROWS, 2/3) como ponto de partida, em que ROWS é o número de linhas de vetor. O número de vetores que cada partição contém é calculado por
    ROWS/power(ROWS, 2/3) = power(ROWS, 1/3).

    Como um índice de árvore de três níveis pode ser criado em um conjunto de dados com mais de 100 milhões de linhas de vetor, cada partição terá mais de
    (power(100M, 1/3)) vetores, que são 465 vetores. Para um desempenho ideal, recomendamos minimizar o número de vetores em cada partição.
Criação de índice
quantizer O tipo de quantizador que você quer usar para a árvore K-means. O valor padrão é SQ8 para melhorar o desempenho da consulta.

Defina como FLAT para melhorar a capacidade de recall.
Criação de índice
enable_pca Ativa a análise de componentes principais (PCA), uma técnica de redução de dimensão usada para reduzir automaticamente o tamanho do encadeamento quando possível. Essa opção é ativada por padrão.

Defina como false se você notar uma deterioração na capacidade de recall.
Criação de índice
scann.num_leaves_to_search A flag do banco de dados controla a compensação entre recall e QPS. O valor padrão é 1% do valor definido em num_leaves.

Quanto maior o valor definido, melhor o recall, mas isso resulta em QPS menor e vice-versa.
Tempo de execução da consulta
scann.max_top_neighbors_buffer_size A flag do banco de dados especifica o tamanho do cache usado para melhorar o desempenho de consultas filtradas, classificando ou ordenando os vizinhos candidatos verificados na memória em vez do disco. O valor padrão é 20000.

Quanto maior o valor definido, melhor será o QPS em consultas filtradas, mas isso resulta em maior uso de memória e vice-versa.
Tempo de execução da consulta
scann.pre_reordering_num_neighbors Quando definida, a flag do banco de dados especifica o número de vizinhos candidatos a serem considerados durante as etapas de reordenação depois que a pesquisa inicial identifica um conjunto de candidatos. Defina um valor maior que o número de vizinhos que você quer que a consulta retorne.

Conjuntos de valores mais altos resultam em um recall melhor, mas essa abordagem resulta em QPS menor.
Tempo de execução da consulta
max_num_levels O número máximo de níveis da árvore de clusterização k-means.
  • Índice de árvore de dois níveis: definido por padrão para quantização baseada em árvore de dois níveis.
  • Índice de árvore de três níveis: definido como 2 explicitamente para quantização baseada em árvore de três níveis.
Criação de índice

Ajustar um índice ScaNN

Confira os exemplos a seguir para índices ScaNN de dois e três níveis que mostram como os parâmetros de ajuste são definidos:

Índice de dois níveis

SET LOCAL scann.num_leaves_to_search = 1;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 1/2)]);

Índice de três níveis

SET LOCAL scann.num_leaves_to_search = 10;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 2/3)], max_num_levels = 2);

Qualquer operação de inserção ou atualização em uma tabela em que um índice ScaNN já foi gerado afeta a forma como a árvore aprendida otimiza o índice. Se a tabela estiver sujeita a atualizações ou inserções frequentes, recomendamos reindexar periodicamente o índice ScaNN atual para melhorar a acurácia de recall.

É possível monitorar as métricas de índice para determinar a quantidade de mutações criadas desde que o índice foi criado e, em seguida, reindexar de acordo. Para mais informações sobre métricas, consulte Métricas de índice vetorial.

Práticas recomendadas para ajuste

As recomendações para ajuste do índice variam de acordo com o tipo de índice ScaNN que você planeja usar. Esta seção oferece recomendações sobre como ajustar os parâmetros de índice para um equilíbrio ideal entre recall e QPS.

Índice de árvore de dois níveis

Para aplicar recomendações que ajudam a encontrar os valores ideais de num_leaves e num_leaves_to_search para seu conjunto de dados, siga estas etapas:

  1. Crie o índice ScaNN com num_leaves definido como a raiz quadrada da contagem de linhas da tabela indexada.
  2. Execute as consultas de teste, aumentando o valor de scann.num_of_leaves_to_search até atingir o intervalo de recall desejado, por exemplo, 95%. Para mais informações sobre como analisar suas consultas, consulte Analisar suas consultas.
  3. Anote a proporção entre scann.num_leaves_to_search e num_leaves, que será usada nas próximas etapas. Essa proporção fornece uma aproximação do conjunto de dados que vai ajudar você a alcançar a meta de recall.

    Se você estiver trabalhando com vetores de alta dimensão (500 dimensões ou mais) e quiser melhorar o recall, ajuste o valor de scann.pre_reordering_num_neighbors. Como ponto de partida, defina o valor como 100 * sqrt(K), em que K é o limite definido na consulta.
  4. Se o QPS estiver muito baixo depois que as consultas atingirem uma taxa de recall desejada, siga estas etapas:
    1. Recrie o índice, aumentando o valor de num_leaves e scann.num_leaves_to_search de acordo com as seguintes orientações:
      • Defina num_leaves como um fator maior da raiz quadrada da contagem de linhas. Por exemplo, se o índice tiver num_leaves definido como a raiz quadrada da contagem de linhas, tente definir como o dobro da raiz quadrada. Se o valor já for duplo, tente definir como o triplo da raiz quadrada.
      • Aumente scann.num_leaves_to_search conforme necessário para manter a proporção com num_leaves, que você anotou na etapa 3.
      • Defina num_leaves como um valor menor ou igual à contagem de linhas dividida por 100.
    2. Execute as consultas de teste novamente. Enquanto executa as consultas de teste, tente reduzir scann.num_leaves_to_search e encontre um valor que aumente as QPS e mantenha o recall alto. Teste valores diferentes de scann.num_leaves_to_search sem recriar o índice.
  5. Repita a etapa 4 até que o QPS e o intervalo de recall atinjam valores aceitáveis.

Índice de árvore de três níveis

Além das recomendações para o índice de árvore de dois níveis ScaNN, use as orientações e etapas a seguir para ajustar o índice:

  • Aumentar o max_num_levels de 1 para uma árvore de dois níveis para 2 para uma árvore de três níveis reduz significativamente o tempo de criação de um índice, mas à custa da acurácia do recall. Defina max_num_levels usando a seguinte recomendação:
    • Defina o valor como 2 se o número de linhas de vetor exceder 100 milhões.
    • Defina o valor como 1 se o número de linhas de vetor for inferior a 10 milhões.
    • Defina como 1 ou 2 se o número de linhas de vetor estiver entre 10 milhões e 100 milhões, com base no equilíbrio entre o tempo de criação do índice e a acurácia de recall necessária.

Para aplicar recomendações e encontrar o valor ideal dos parâmetros de índice num_leaves e max_num_levels, siga estas etapas:

  1. Crie o índice ScaNN com as seguintes combinações de num_leaves e max_num_levels com base no seu conjunto de dados:

    • linhas de vetor maiores que 100 milhões de linhas: defina max_num_levels como 2 e num_leaves como power(rows, ⅔).
    • linhas de vetor menores que 100 milhões de linhas: defina max_num_levels como 1 e num_leaves como sqrt(rows).
    • linhas de vetor entre 10 milhões e 100 milhões de linhas: comece definindo max_num_levels como 1 e num_leaves como sqrt(rows).
  2. Execute suas consultas de teste. Para mais informações sobre como analisar consultas, consulte Analisar suas consultas.

    Se o tempo de criação do índice for satisfatório, mantenha o valor max_num_levels e teste o valor num_leaves para ter uma acurácia de recall ideal.

  3. Se você não estiver satisfeito com o tempo de criação do índice, faça o seguinte:

    • Se o valor de max_num_levels for 1, descarte o índice. Reconstrua o índice com o valor max_num_levels definido como 2.

      Execute as consultas e ajuste o valor de num_leaves para ter uma acurácia de recall ideal.

    • Se o valor de max_num_levels for 2, descarte o índice. Recrie o índice com o mesmo valor max_num_levels e ajuste o valor num_leaves para uma acurácia de recall ideal.

Ajustar um índice IVF

Ajustar os valores definidos para os parâmetros lists, ivf.probes e quantizer pode ajudar a otimizar o desempenho do aplicativo:

Parâmetro de ajuste Descrição Tipo de parâmetro
lists O número de listas criadas durante a criação do índice. O ponto de partida para definir esse valor é (rows)/1000 para até um milhão de linhas e sqrt(rows) para mais de um milhão de linhas. Criação de índice
quantizer O tipo de quantizador que você quer usar para a árvore K-means. O valor padrão é SQ8 para melhorar o desempenho da consulta. Defina como FLAT para facilitar a memorização. Criação de índice
ivf.probes o número de listas mais próximas a serem analisadas durante a pesquisa. O ponto de partida desse valor é
sqrt(lists).
Tempo de execução da consulta

Considere o exemplo a seguir, que mostra um índice IVF com os parâmetros de ajuste definidos:

SET LOCAL ivf.probes = 10;

CREATE INDEX my-ivf-index ON my-table
  USING ivf (vector_column cosine)
  WITH (lists = 100, quantizer = 'SQ8');

Ajustar um índice IVFFlat

Ajustar os valores definidos para os parâmetros lists e ivfflat.probes pode ajudar a otimizar o desempenho do aplicativo:

Parâmetro de ajuste Descrição Tipo de parâmetro
lists O número de listas criadas durante a criação do índice. O ponto de partida para definir esse valor é (rows)/1000 para até um milhão de linhas e sqrt(rows) para mais de um milhão de linhas. Criação de índice
ivfflat.probes O número de listas mais próximas a serem analisadas durante a pesquisa. O ponto de partida desse valor é
sqrt(lists).
Tempo de execução da consulta

Antes de criar um índice IVFFlat, verifique se a flag max_parallel_maintenance_workers do banco de dados está definida com um valor suficiente para acelerar a criação do índice em tabelas grandes.

Considere o exemplo a seguir, que mostra um índice IVFFlat com os parâmetros de ajuste definidos:

SET LOCAL ivfflat.probes = 10;

CREATE INDEX my-ivfflat-index ON my-table
  USING ivfflat (vector_column cosine)
  WITH (lists = 100);

Ajustar um índice HNSW

Ajustar os valores definidos para os parâmetros m, ef_construction e hnsw.ef_search pode ajudar a otimizar a performance do aplicativo.

Parâmetro de ajuste Descrição Tipo de parâmetro
m O número máximo de conexões de um nó no gráfico. Comece com o valor padrão 16(padrão) e teste valores mais altos com base no tamanho do seu conjunto de dados. Criação de índice
ef_construction O tamanho da lista dinâmica de candidatos mantida durante a construção do gráfico, que atualiza constantemente os melhores candidatos atuais para vizinhos mais próximos de um nó. Defina esse valor como qualquer valor maior que o dobro do valor de m, por exemplo, 64(padrão). Criação de índice
ef_search O tamanho da lista dinâmica de candidatos usada durante a pesquisa. Comece definindo esse valor como m ou ef_construction e mude enquanto observa o recall. O valor padrão é 40. Tempo de execução da consulta

Considere o exemplo a seguir, que mostra um índice hnsw com os parâmetros de ajuste definidos:

SET LOCAL hnsw.ef_search = 40;

CREATE INDEX my-hnsw-index ON my-table
  USING hnsw (vector_column cosine)
  WITH (m = 16, ef_construction = 200);

Analisar suas consultas

Use o comando EXPLAIN ANALYZE para analisar os insights de consulta, conforme mostrado no exemplo de consulta SQL a seguir.

  EXPLAIN ANALYZE SELECT result-column FROM my-table
    ORDER BY EMBEDDING_COLUMN ::vector
    USING INDEX my-scann-index
    <-> embedding('textembedding-gecko@003', 'What is a database?')
    LIMIT 1;

O exemplo de resposta QUERY PLAN inclui informações como o tempo gasto, o número de linhas verificadas ou retornadas e os recursos usados.

Limit  (cost=0.42..15.27 rows=1 width=32) (actual time=0.106..0.132 rows=1 loops=1)
  ->  Index Scan using my-scann-index on my-table  (cost=0.42..858027.93 rows=100000 width=32) (actual time=0.105..0.129 rows=1 loops=1)
        Order By: (embedding_column <-> embedding('textgecko@003', 'What is a database?')::vector(768))
        Limit value: 1
Planning Time: 0.354 ms
Execution Time: 0.141 ms

Visualizar métricas de índice vetorial

Use as métricas de índice de vetor para analisar a performance dele, identificar áreas de melhoria e ajustar o índice com base nas métricas, se necessário.

Para conferir todas as métricas de índice de vetor, execute a seguinte consulta SQL, que usa a visualização pg_stat_ann_indexes:

SELECT * FROM pg_stat_ann_indexes;

Para mais informações sobre a lista completa de métricas, consulte Métricas de índice vetorial.

A seguir