Otimize o desempenho das consultas vetoriais

Selecione uma versão da documentação:

Este documento mostra como ajustar os seus índices para alcançar um desempenho de consulta mais rápido e uma melhor capacidade de memorização.

Ajuste um índice ScaNN

O índice ScaNN usa a indexação baseada na quantização de árvores. Nas técnicas de quantização de árvores, os índices aprendem uma árvore de pesquisa juntamente com uma função de quantização (ou hash). Quando executa uma consulta, a árvore de pesquisa é usada para reduzir o espaço de pesquisa, enquanto a quantização é usada para comprimir o tamanho do índice. Esta remoção acelera a classificação da semelhança (ou seja, a distância) entre o vetor de consulta e os vetores da base de dados.

Para alcançar uma taxa de consultas por segundo (QPS) elevada e uma elevada capacidade de memorização com as suas consultas de vizinho mais próximo, tem de particionar a árvore do seu índice ScaNN da forma mais adequada aos seus dados e consultas.

Antes de criar um índice ScaNN, conclua o seguinte:

  • Certifique-se de que já criou uma tabela com os seus dados.
  • Certifique-se de que o valor que define para o indicador maintenance_work_mem e shared_buffers é inferior à memória total da máquina para evitar problemas ao gerar o índice.

Parâmetros de aperfeiçoamento

Os seguintes parâmetros de índice e flags da base de dados são usados em conjunto para encontrar o equilíbrio certo entre a capacidade de memorização e as QPS. Todos os parâmetros aplicam-se a ambos os tipos de índice ScaNN.

Parâmetro de aperfeiçoamento Descrição Tipo de parâmetro
num_leaves O número de partições a aplicar a este índice. O número de partições que aplica quando cria um índice afeta o desempenho do índice. Ao aumentar as partições para um número definido de vetores, cria um índice mais detalhado, o que melhora a capacidade de memorização e o desempenho das consultas. No entanto, isto tem o custo de tempos de criação de índices mais longos.

Uma vez que as árvores de três níveis são criadas mais rapidamente do que as árvores de dois níveis, pode aumentar o num_leaves_value quando cria um índice de árvore de três níveis para alcançar um melhor desempenho.
  • Índice de dois níveis: defina este valor para qualquer valor entre 1 e 1048576.

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

    Uma vez que é possível criar um índice de árvore de dois níveis num conjunto de dados com menos de 10 milhões de linhas de vetores, cada partição contém menos de (sqrt(10M)) vetores, ou seja, 3200 vetores. Para um desempenho ideal, recomendamos que minimize o número de vetores em cada partição.
  • Índice de três níveis: defina este valor para qualquer valor entre 1 e 1048576.

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

    Uma vez que é possível criar um índice de árvore de três níveis num conjunto de dados com mais de 100 milhões de linhas de vetores, cada partição contém mais de
    (power(100M, 1/3)) vetores, o que equivale a 465 vetores. Para um desempenho ideal, recomendamos que minimize o número de vetores em cada partição.
Criação de índice
quantizer O tipo de quantizador que quer usar para a árvore K-means. O valor predefinido é SQ8 para um melhor desempenho das consultas.

Defina-o como FLAT para uma melhor capacidade de memorização.
Criação de índice
enable_pca Ativa a análise de componentes principais (PCA), que é uma técnica de redução de dimensões usada para reduzir automaticamente o tamanho da incorporação sempre que possível. Esta opção está ativada por predefinição.

Defina como false se observar uma deterioração na capacidade de memorização.
Criação de índice
scann.num_leaves_to_search O indicador da base de dados controla a compensação entre a capacidade de memorização e as QPS. O valor predefinido é 1% do valor definido em num_leaves.

Quanto mais alto for o valor definido, melhor é a capacidade de memorização, mas resulta num QPS mais baixo e vice-versa.
Tempo de execução da consulta
scann.max_top_neighbors_buffer_size A flag da base de dados especifica o tamanho da cache usada para melhorar o desempenho das consultas filtradas através da classificação dos vizinhos candidatos analisados na memória em vez do disco. O valor predefinido é 20000.

Quanto mais elevado for o valor definido, melhor é o QPS em consultas filtradas, mas resulta numa maior utilização de memória e vice-versa.
Tempo de execução da consulta
scann.pre_reordering_num_neighbors Quando definida, a flag da base de dados especifica o número de vizinhos candidatos a considerar durante as fases de reordenação depois de a pesquisa inicial identificar um conjunto de candidatos. Defina este valor como superior ao número de vizinhos que quer que a consulta devolva.

Os conjuntos de valores mais elevados resultam numa melhor recordação, mas esta abordagem resulta num QPS mais baixo.
Tempo de execução da consulta
max_num_levels O número máximo de níveis da árvore de agrupamento K-means.
  • Índice de árvore de dois níveis: definido por predefinição para a quantização baseada em árvores de dois níveis.
  • Índice de árvore de três níveis: definido como 2 explicitamente para a quantização baseada em árvores de três níveis.
Criação de índice

Ajuste um índice ScaNN

Considere os seguintes exemplos para índices de ScaNN de dois e três níveis que mostram como os parâmetros de otimização 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 numa tabela em que já tenha sido gerado um índice ScaNN afeta a forma como a árvore de aprendizagem otimiza o índice. Se a sua tabela for propensa a atualizações ou inserções frequentes, recomendamos que reindexe periodicamente o índice ScaNN existente para melhorar a precisão da obtenção.

Pode monitorizar as métricas de índice para determinar a quantidade de mutações criadas desde a criação do índice e, em seguida, reindexar em conformidade. Para mais informações sobre as métricas, consulte as métricas do índice de vetores.

Práticas recomendadas para a otimização

Com base no tipo de índice ScaNN que planeia usar, as recomendações para otimizar o índice variam. Esta secção fornece recomendações sobre como ajustar os parâmetros de índice para um equilíbrio ideal entre a capacidade de memorização e as 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 o seu conjunto de dados, siga estes passos:

  1. Crie o índice ScaNN com num_leaves definido como a raiz quadrada da contagem de linhas da tabela indexada.
  2. Execute as suas consultas de teste, aumentando o valor de scann.num_of_leaves_to_search, até atingir o intervalo de recall alvo, por exemplo, 95%. Para mais informações sobre a análise das suas consultas, consulte o artigo Analise as suas consultas.
  3. Tome nota da proporção entre scann.num_leaves_to_search e num_leaves que vai ser usada nos passos seguintes. Esta proporção fornece uma aproximação em torno do conjunto de dados que vai ajudar a alcançar a sua capacidade de memorização alvo.

    Se estiver a trabalhar com vetores de dimensões elevadas (500 dimensões ou mais) e quiser melhorar a capacidade de memorização, experimente ajustar o valor de scann.pre_reordering_num_neighbors. Como ponto de partida, defina o valor como 100 * sqrt(K), em que K é o limite que definiu na consulta.
  4. Se o seu QPS for demasiado baixo depois de as suas consultas atingirem uma taxa de obtenção alvo, siga estes passos:
    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 para 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, experimente defini-lo como o dobro da raiz quadrada. Se o valor já for o dobro, experimente defini-lo como o triplo da raiz quadrada.
      • Aumente scann.num_leaves_to_search conforme necessário para manter a respetiva proporção com num_leaves, que indicou no passo 3.
      • Defina num_leaves para um valor inferior ou igual à contagem de linhas dividida por 100.
    2. Execute novamente as consultas de teste. Enquanto executa as consultas de teste, experimente reduzir scann.num_leaves_to_search e encontrar um valor que aumente as QPS, mantendo a taxa de recordação elevada. Experimente valores diferentes de scann.num_leaves_to_search sem reconstruir o índice.
  5. Repita o passo 4 até o QPS e o intervalo de recolha terem atingido valores aceitáveis.

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

Além das recomendações para o índice de árvore de ScaNN níveis, use as seguintes orientações e os passos para otimizar o índice:

  • Aumentar o valor de 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 precisão da capacidade de recordar. Defina max_num_levels com a seguinte recomendação:
    • Defina o valor como 2 se o número de linhas vetoriais exceder 100 milhões de linhas.
    • Defina o valor como 1 se o número de linhas de vetores for inferior a 10 milhões de linhas.
    • Defina como 1 ou 2 se o número de linhas de vetores estiver entre 10 milhões e 100 milhões de linhas, com base no equilíbrio entre o tempo de criação do índice e a precisão de recolha de que precisa.

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

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

    • vector rows greater than 100 million rows: defina max_num_levels como 2 e num_leaves como power(rows, ⅔).
    • linhas de vetores com menos de 100 milhões de linhas: defina max_num_levels como 1 e num_leaves como sqrt(rows).
    • Linhas de vetores entre 10 milhões e 100 milhões de linhas: comece por definir max_num_levels como 1 e num_leaves como sqrt(rows).
  2. Execute as suas consultas de teste. Para mais informações sobre a análise de consultas, consulte o artigo Analise as suas consultas.

    Se o tempo de criação do índice for satisfatório, mantenha o valor max_num_levels e experimente o valor num_leaves para uma precisão de memorização ideal.

  3. Se 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, elimine 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 uma precisão de memorização ideal.

    • Se o valor max_num_levels for 2, elimine o índice. Reconstrua o índice com o mesmo valor max_num_levels e ajuste o valor num_leaves para uma precisão de recolha ideal.

Ajuste um índice IVF

Ajustar os valores que define para os parâmetros lists, ivf.probes e quantizer pode ajudar a otimizar o desempenho da sua aplicação:

Parâmetro de aperfeiçoamento 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 este 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 quer usar para a árvore K-means. O valor predefinido é SQ8 para um melhor desempenho das consultas. Defina-o como FLAT para uma melhor memorização. Criação de índice
ivf.probes O número de listas mais próximas a explorar durante a pesquisa. O ponto de partida para este valor é
sqrt(lists).
Tempo de execução da consulta

Considere o exemplo seguinte que mostra um índice IVF com os parâmetros de otimização definidos:

SET LOCAL ivf.probes = 10;

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

Ajuste um índice IVFFlat

Ajustar os valores que define para os parâmetros lists e ivfflat.probes pode ajudar a otimizar o desempenho da aplicação:

Parâmetro de aperfeiçoamento 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 este 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 explorar durante a pesquisa. O ponto de partida para este valor é
sqrt(lists).
Tempo de execução da consulta

Antes de criar um índice IVFFlat, certifique-se de que a flag max_parallel_maintenance_workers da sua base de dados está definida para um valor suficiente para acelerar a criação do índice em tabelas grandes.

Considere o exemplo seguinte que mostra um índice IVFFlat com os parâmetros de otimização definidos:

SET LOCAL ivfflat.probes = 10;

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

Ajuste um índice HNSW

Ajustar os valores que define para os parâmetros m, ef_construction e hnsw.ef_search pode ajudar a otimizar o desempenho da aplicação.

Parâmetro de aperfeiçoamento Descrição Tipo de parâmetro
m O número máximo de associações por nó no gráfico. Pode começar com o valor predefinido 16(predefinição) e experimentar valores mais elevados com base no tamanho do seu conjunto de dados. Criação de índice
ef_construction O tamanho da lista de candidatos dinâmicos mantida durante a construção do gráfico, que atualiza constantemente os melhores candidatos atuais para os vizinhos mais próximos de um nó. Defina este valor para qualquer valor superior ao dobro do valor m, por exemplo, 64(predefinição). Criação de índice
ef_search O tamanho da lista de candidatos dinâmicos usada durante a pesquisa. Pode começar a definir este valor como m ou ef_construction e, em seguida, alterá-lo enquanto observa a recolha. O valor predefinido é 40. Tempo de execução da consulta

Considere o exemplo seguinte que mostra um índice hnsw com os parâmetros de otimização 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);

Analise as suas consultas

Use o comando EXPLAIN ANALYZE para analisar as estatísticas de consultas, conforme mostrado no seguinte exemplo de consulta SQL.

  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;

A resposta de exemplo QUERY PLAN inclui informações como o tempo necessário, o número de linhas analisadas ou devolvidas 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

Veja métricas do índice vetorial

Pode usar as métricas do índice vetorial para rever o desempenho do índice vetorial, identificar áreas de melhoria e ajustar o índice com base nas métricas, se necessário.

Para ver todas as métricas do índice vetorial, execute a seguinte consulta SQL, que usa a vista pg_stat_ann_indexes:

SELECT * FROM pg_stat_ann_indexes;

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

O que se segue?