Criar um índice do ScaNN

Os índices Scalable Nearest Neighbors (ScaNN) no AlloyDB para PostgreSQL permitem acelerar as pesquisas vetoriais de vizinho mais próximo aproximado em embeddings armazenados.

O índice ScaNN é um índice de quantização baseado em árvore criado pelo Google para pesquisa de vizinho mais próximo aproximado. Ele oferece um tempo de criação de índice menor e um consumo de memória menor em comparação com o HNSW. Além disso, ele oferece QPS mais rápido em comparação com o HNSW, com base na carga de trabalho.

Antes de começar

Antes de começar a criar índices, você precisa atender aos seguintes pré-requisitos.

Criar um índice ajustado automaticamente

Os índices ScaNN ajustados automaticamente simplificam a criação de índices, permitindo que o AlloyDB gerencie e ajuste a estrutura do índice. Se você precisar de controle granular sobre o ajuste de índice, crie um índice ScaNN ajustado manualmente.

Os índices ajustados automaticamente podem ser otimizados de duas maneiras:

  • (Padrão) Recall e latência da pesquisa vetorial ao custo do tempo de build do índice
  • Equilibrar o tempo de build do índice e a performance da pesquisa

Para criar um índice ScaNN ajustado automaticamente, execute o comando a seguir.

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)

Substitua:

  • INDEX_NAME: nome do índice que você quer criar. Por exemplo, my_scann_index. Os nomes de índice são compartilhados em todo o banco de dados. Verifique se cada nome de índice é exclusivo para cada tabela no banco de dados.

  • TABLE: tabela em que o índice será adicionado.

  • EMBEDDING_COLUMN: coluna que armazena dados de vector.

  • DISTANCE_FUNCTION: função de distância a ser usada com esse índice. Escolha uma destas opções:

    • Distância de L2: l2

    • Produto escalar: dot_product

    • Distância do cosseno: cosine

Esse comando cria um índice ScaNN otimizado para desempenho de pesquisa e realiza a manutenção automática do índice. Se você quiser mudar uma dessas configurações, execute o seguinte comando:

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (MODE='AUTO',
      OPTIMIZATION='OPTIMIZATION',
      auto_maintenance='AUTO_MAINTENANCE')

Substitua:

  • INDEX_NAME: nome do índice que você quer criar. Por exemplo, my_scann_index. Os nomes de índice são compartilhados em todo o banco de dados. Verifique se cada nome de índice é exclusivo para cada tabela no banco de dados.

  • TABLE: tabela em que o índice será adicionado.

  • EMBEDDING_COLUMN: coluna que armazena dados de vector.

  • DISTANCE_FUNCTION: função de distância a ser usada com esse índice. Escolha uma destas opções:

    • Distância de L2: l2

    • Produto escalar: dot_product

    • Distância do cosseno: cosine

  • (Opcional) OPTIMIZATION: defina como um dos seguintes:

    • (Padrão) SEARCH_OPTIMIZED: otimiza o recall e a latência da pesquisa vetorial, mas aumenta o tempo de criação do índice.

    • BALANCED: equilibra o tempo de criação do índice e o desempenho da pesquisa.

    Se OPTIMIZATION estiver definido, MODE='AUTO' também precisará ser incluído.

  • (Opcional) AUTO_MAINTENANCE: controla se a manutenção automática do índice está ativada ou desativada. Para mais informações sobre manutenção automática, consulte Manter índices vetoriais.

    • (Padrão) ON: o AlloyDB realiza a manutenção automática do índice.

    • OFF: o AlloyDB não realiza manutenção automática no índice.

Criar um índice ajustado manualmente

Se o aplicativo tiver requisitos específicos para tempos de recall e de build de índice, será possível criar e ajustar manualmente o índice do ScaNN.

Para criar manualmente um índice ScaNN para uma coluna que contém embeddings de vetor armazenados, consulte os comandos a seguir.

Índice de árvore de dois níveis

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE);
  • INDEX_NAME: nome do índice que você quer criar. Por exemplo, my_scann_index. Os nomes de índice são compartilhados em todo o banco de dados. Verifique se cada nome de índice é exclusivo para cada tabela no banco de dados.
  • TABLE: tabela em que o índice será adicionado.
  • EMBEDDING_COLUMN: coluna que armazena dados de "vetor".
  • DISTANCE_FUNCTION: função de distância a ser usada com esse índice. Escolha uma das opções a seguir:
    • Distância de L2: l2
    • Produto escalar: dot_product
    • Distância do cosseno: cosine
  • NUM_LEAVES_VALUE: número de partições a serem aplicadas a esse índice. Defina como qualquer valor entre 1 e 30 milhões. Para mais informações sobre como escolher esse valor, consulte Ajustar um índice ScaNN.
  • QUANTIZER: tipo de quantizador a ser usado. O índice ScaNN pode ser carregado no mecanismo colunar para acelerar ainda mais a busca vetorial. Escolha uma das opções a seguir:
    • (Padrão) SQ8: oferece um equilíbrio entre o desempenho da consulta e a perda mínima de recall. Normalmente, isso é menos de 1 a 2%.
    • Prévia AH: o hash assimétrico (AH) é até quatro vezes mais compactado em comparação com SQ8. Para um desempenho potencialmente melhor da consulta quando o mecanismo colunar está ativado e os dados de índice e tabela são preenchidos no mecanismo colunar, considere isso para um desempenho potencialmente melhor da consulta. Para mais informações, consulte Práticas recomendadas para ajustar o ScaNN.
    • FLAT: oferece o maior recall, 99% ou mais, ao custo do desempenho da pesquisa.
  • (Opcional) AUTO_MAINTENANCE: controla se a manutenção automática no índice está ativada ou desativada. Para mais informações sobre manutenção automática, consulte Manter índices vetoriais.
    • (Padrão) ON: o AlloyDB realiza a manutenção automática no índice.
    • OFF: o AlloyDB não realiza manutenção automática no índice.

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

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE,
      max_num_levels = 2);
  • INDEX_NAME: nome do índice que você quer criar. Por exemplo, my_scann_index. Os nomes de índice são compartilhados em todo o banco de dados. Verifique se cada nome de índice é exclusivo para cada tabela no banco de dados.
  • TABLE: tabela em que o índice será adicionado.
  • EMBEDDING_COLUMN: coluna que armazena dados de "vetor".
  • DISTANCE_FUNCTION: função de distância a ser usada com esse índice. Escolha uma das opções a seguir:
    • Distância de L2: l2
    • Produto escalar: dot_product
    • Distância do cosseno: cosine
  • NUM_LEAVES_VALUE: número de partições a serem aplicadas a esse índice. Defina como qualquer valor entre 1 e 30 milhões. Para mais informações sobre como escolher esse valor, consulte Ajustar um índice ScaNN.
  • QUANTIZER: tipo de quantizador a ser usado. O índice ScaNN pode ser carregado no mecanismo colunar para acelerar ainda mais a pesquisa vetorial. Escolha uma das opções a seguir:
    • (Padrão) SQ8: oferece um equilíbrio entre o desempenho da consulta e a perda mínima de recall. Isso geralmente é inferior a 1% a 2%.
    • Pré-lançamento AH: o hash assimétrico (AH) é até 4 vezes mais compactado em comparação com SQ8. Para melhorar o desempenho da consulta quando o mecanismo colunar estiver ativado e os dados do índice e da tabela forem preenchidos no mecanismo colunar, considere isso para melhorar o desempenho da consulta. Para mais informações, consulte Práticas recomendadas para ajustar o ScaNN.
    • FLAT: oferece o maior recall, 99% ou mais, ao custo do desempenho da pesquisa.
  • (Opcional) AUTO_MAINTENANCE: controla se a manutenção automática do índice está ativada ou desativada. Para mais informações sobre manutenção automática, consulte Manter índices vetoriais.
    • (Padrão) ON: o AlloyDB realiza a manutenção automática no índice.
    • OFF: o AlloyDB não realiza manutenção automática no índice.
  • max_num_levels = 2: o número máximo de níveis de centroide da árvore de clustering k-means. Defina esse parâmetro como 2 para criar um índice de três níveis.

Índice de árvore de quatro níveis

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE,
      max_num_levels = 3);
  • INDEX_NAME: nome do índice que você quer criar. Por exemplo, my_scann_index. Os nomes de índice são compartilhados em todo o banco de dados. Verifique se cada nome de índice é exclusivo para cada tabela no banco de dados.
  • TABLE: tabela em que o índice será adicionado.
  • EMBEDDING_COLUMN: coluna que armazena dados de "vetor".
  • DISTANCE_FUNCTION: função de distância a ser usada com esse índice. Escolha uma das opções a seguir:
    • Distância de L2: l2
    • Produto escalar: dot_product
    • Distância do cosseno: cosine
  • NUM_LEAVES_VALUE: número de partições a serem aplicadas a esse índice. Definido como qualquer valor entre 1 e 30 milhões. Para mais informações sobre como escolher esse valor, consulte Ajustar um índice ScaNN.
  • QUANTIZER: tipo de quantizador a ser usado. O índice ScaNN pode ser carregado no mecanismo colunar para acelerar ainda mais a pesquisa vetorial. Escolha uma das opções a seguir:
    • (Padrão) SQ8: oferece um equilíbrio entre o desempenho da consulta e a perda mínima de recall. Normalmente, isso é menos de 1 a 2%.
    • Prévia AH: o hash assimétrico (AH) é até quatro vezes mais compactado em comparação com SQ8. Para um desempenho potencialmente melhor da consulta quando o mecanismo colunar está ativado e os dados de índice e tabela são preenchidos no mecanismo colunar, considere isso para um desempenho potencialmente melhor da consulta. Para mais informações, consulte Práticas recomendadas para ajustar o ScaNN.
    • FLAT: oferece o maior recall, 99% ou mais, ao custo do desempenho da pesquisa.
  • (Opcional) AUTO_MAINTENANCE: controla se a manutenção automática do índice está ativada ou desativada. Para mais informações sobre a manutenção automática, consulte Manter índices vetoriais.
    • (Padrão) ON: o AlloyDB realiza a manutenção automática do índice.
    • OFF: o AlloyDB não realiza manutenção automática no índice.
  • max_num_levels = 3: o número máximo de níveis de centroide da árvore de clustering k-means. Defina esse parâmetro como 3 para criar um índice de quatro níveis.

Converter um índice ajustado manualmente em um índice ajustado automaticamente

Para converter um índice ajustado manualmente em um ajustado automaticamente, siga estas etapas:

  1. Redefina todos os parâmetros de consulta definidos para o índice ajustado manualmente.

    ALTER INDEX INDEX_NAME RESET (PARAMETER_NAME);
    

    Substitua as seguintes variáveis:

    • INDEX_NAME: nome do índice que você quer converter. Por exemplo, my_scann_index. Os nomes de índice são compartilhados em todo o banco de dados. Verifique se cada nome de índice é exclusivo para cada tabela no banco de dados.

    • PARAMETER_NAME: lista separada por vírgulas que contém os nomes dos parâmetros de consulta que você quer redefinir. Por exemplo, num_leaves, quantization.

      É necessário redefinir todos os outros parâmetros de consulta antes de redefinir num_leaves.

  2. Reindexe o índice ajustado manualmente para convertê-lo em um índice ajustado automaticamente.

    REINDEX INDEX CONCURRENTLY INDEX_NAME;
    

Criar um índice ScaNN para tipos de dados real[]

Para criar um índice para uma coluna de embedding que usa o tipo de dados real[] em vez de vector, converta a coluna no tipo de dados vector:

CREATE INDEX INDEX_NAME ON TABLE
USING scann (CAST(EMBEDDING_COLUMN AS vector(DIMENSIONS)) DISTANCE_FUNCTION)

Substitua:

  • INDEX_NAME: nome do índice que você quer criar. Por exemplo, my_scann_index. Os nomes de índice são compartilhados em todo o banco de dados. Verifique se cada nome de índice é exclusivo para cada tabela no banco de dados.

  • TABLE: tabela em que o índice será adicionado.

  • DIMENSIONS: o número de dimensões que o modelo aceita.

  • EMBEDDING_COLUMN: coluna que armazena dados de vector.

  • DISTANCE_FUNCTION: função de distância a ser usada com esse índice. Escolha uma destas opções:

    • Distância de L2: l2

    • Produto escalar: dot_product

    • Distância do cosseno: cosine

Ver o progresso da indexação

Para conferir o progresso da indexação, use a visualização pg_stat_progress_create_index:

SELECT * FROM pg_stat_progress_create_index;

A coluna phase mostra o estado atual da criação do índice. Depois que a fase de criação do índice for concluída, a linha do índice não vai ficar visível.

Criar um índice adiado para tabelas vazias ou com linhas insuficientes

Por padrão, não é possível criar um índice ScaNN em uma tabela vazia ou que tenha menos linhas do que o valor da opção de índice num_leaves.

Para contornar essa limitação, ative a criação de índice adiada para permitir que o AlloyDB adie a criação de índice até que o número de linhas na tabela atinja o limite definido por num_leaves. Depois que o limite é atingido, o AlloyDB inicia a criação do índice em segundo plano.

Essa operação adiada é um processo não bloqueador, permitindo que outras operações de banco de dados, como leituras e gravações, continuem sem interrupção. Como a reconstrução do índice acontece em segundo plano, a criação de índice adiada é adequada quando as tabelas ingerem linhas de dados em pequenos lotes. A recriação do índice é acionada automaticamente quando o número de linhas atinge o limite.

No entanto, se você planeja inserir um grande número de linhas na tabela em uma única transação, recomendamos dividir a transação em várias ou gerar um índice ScaNN sem ativar a criação adiada de índice.

Ativar a criação de índice adiada

Para ativar a criação de índice adiada, siga estas etapas:

  1. Verifique se a flag scann.enable_index_maintenance e uma das seguintes flags estão ativadas:

    Para configurar o uso, use a CLI gcloud:

    • Para ativar a criação de índice adiada com outros recursos em fase de pré-lançamento:

      gcloud alloydb instances update INSTANCE_ID \
         --database-flags scann.enable_index_maintenance=on \
         --database-flags scann.enable_preview_features=on \
         --region=REGION_ID \
         --cluster=CLUSTER_ID \
         --project=PROJECT_ID
      
    • Para ativar explicitamente a criação de índice adiada sem ativar outros recursos em fase de pré-lançamento:

      gcloud alloydb instances update INSTANCE_ID \
         --database-flags scann.enable_index_maintenance=on \
         --database-flags scann.enable_index_with_insufficient_data=on \
         --region=REGION_ID \
         --cluster=CLUSTER_ID \
         --project=PROJECT_ID
      

    Substitua:

    • INSTANCE_ID: o ID da instância.
    • REGION_ID: a região em que a instância está localizada, por exemplo, us-central1.
    • CLUSTER_ID: o ID do cluster em que a instância está localizada.
    • PROJECT_ID: o ID do projeto em que o cluster está localizado.
  2. Crie um índice ScaNN:

    • Para criar um índice com ajuste automático, especifique explicitamente WITH (MODE='AUTO') na instrução CREATE INDEX:

      CREATE INDEX INDEX_NAME ON TABLE
             USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
      WITH (MODE='AUTO')
      

      Para mais informações sobre os parâmetros, consulte Criar um índice ajustado automaticamente.

    • Para criar um índice ajustado manualmente, verifique se o parâmetro auto_maintenance está definido como on. Para mais informações, consulte Criar um índice ajustado manualmente.

Limitações

  • O processo em segundo plano de criação automática de índices usa valores de flag no nível do banco de dados. Mesmo que você defina flags no nível da sessão usando o comando SET LOCAL, o processo considera o valor da flag definido no nível do banco de dados.
  • Se você planeja inserir em massa uma grande quantidade de dados em uma tabela vazia em uma única transação, recomendamos executar a transação de inserção única e, em seguida, criar um índice ScaNN.

Forçar a criação de índices em tabelas vazias ou pequenas

O AlloyDB usa validações para impedir a criação de um índice ScaNN em uma tabela vazia ou com poucas linhas pelos seguintes motivos:

  • O índice do ScaNN é treinado com dados insuficientes. Isso pode resultar em um recall ruim para pesquisas de similaridade de vetor.

  • A performance de gravação no banco de dados pode ser prejudicada.

Recomendamos que você atrase a criação de índices em desempenho abaixo do ideal.

No entanto, em alguns cenários de desenvolvimento ou teste, talvez seja necessário criar um índice em uma tabela vazia ou pequena. Nesses casos, é possível aplicar a criação de índices. Para forçar a criação de índices, é preciso ter privilégios de SUPERUSER.

Para forçar a criação do índice, siga estas etapas:

  1. Defina o parâmetro scann.allow_blocked_operations no nível da sessão como true no banco de dados:

    SET scann.allow_blocked_operations = true;
    
  2. Se o usuário que você está usando para executar essas consultas não tiver privilégios SUPERUSER, atribua-os:

    CREATE USER USERNAME WITH SUPERUSER PASSWORD PASSWORD;
    

    Substitua as seguintes variáveis:

    • USERNAME: nome do usuário a quem você quer conceder privilégios de SUPERUSER.
    • PASSWORD: senha do usuário.

Criar índices em paralelo

Para criar o índice mais rapidamente, o AlloyDB pode gerar automaticamente vários workers paralelos, dependendo do conjunto de dados e do tipo de índice escolhido. Isso geralmente acontece ao criar um índice ScaNN de três ou quatro níveis ou se o conjunto de dados exceder 100 milhões de linhas.

Embora o AlloyDB otimize automaticamente o número de workers paralelos, é possível ajustar esses workers usando os seguintes parâmetros de planejamento de consultas do PostgreSQL:

Para evitar problemas de falta de memória ao criar o índice ScaNN, verifique se as flags de banco de dados maintenance_work_mem e shared_buffers estão definidas com um valor menor que a memória total da máquina.

Executar uma consulta

Depois de armazenar e indexar os embeddings no banco de dados, você pode começar a consultar os dados. Não é possível executar consultas de pesquisa em massa usando a extensão alloydb_scann.

Para encontrar os vizinhos semânticos mais próximos de uma string de texto, use a função google_ml.embedding() para converter o texto em um vetor.

Como google_ml.embedding() retorna uma matriz real, é necessário transmitir explicitamente a chamada de função para vector antes de aplicá-la a um dos operadores de vizinho mais próximo, por exemplo, <-> para a distância L2. Esses operadores podem usar o índice ScaNN para encontrar as linhas do banco de dados com os embeddings mais semanticamente semelhantes.

SELECT * FROM TABLE
ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY
  google_ml.embedding(
      model_id => 'MODEL_ID',
      content => 'CONTENT')::vector
LIMIT ROW_COUNT

Substitua as seguintes variáveis:

  • TABLE: tabela que contém o embedding com o qual você vai comparar o texto.

  • EMBEDDING_COLUMN: coluna que contém os embeddings armazenados.

  • DISTANCE_FUNCTION_QUERY: função de distância a ser usada com essa consulta. Escolha a consulta equivalente à função de distância ao criar o índice:

    • Distância de L2: <->

    • Produto interno: <#>

    • Distância do cosseno: <=>

  • MODEL_ID: ID do modelo de embedding registrado que você quer usar.

  • CONTENT: a string de texto que você quer traduzir em um embedding e pesquisar.

  • ROW_COUNT: número de linhas a serem retornadas. Por exemplo, especifique 1 se você quiser apenas a melhor correspondência.

A seguir