Criar um índice do ScaNN

Use embeddings armazenados para gerar índices de vetor do ScaNN e consultar embeddings com o AlloyDB para PostgreSQL.

O índice ScaNN é um índice de quantização baseado em árvore criado pelo Google para pesquisa aproximada de vizinho mais próximo. 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, conclua os 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 do í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 seguinte comando:

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 o desempenho da pesquisa e realiza a manutenção automática do índice. Se você quiser mudar qualquer 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 de vetor, mas aumenta o tempo de criação do índice.

    • BALANCED: equilibra o tempo de build do índice e o desempenho da pesquisa.

    Se OPTIMIZATION for 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 a manutenção automática, consulte Manter índices vetoriais.

    • (Padrão) ON: o AlloyDB realiza manutenção automática no í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 recall e tempos de criação de índice, crie e ajuste 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. 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 do ScaNN pode ser carregado no mecanismo colunar para acelerar ainda mais a pesquisa de vetor. Escolha uma das opções a seguir:
    • (Padrão) SQ8: oferece um equilíbrio entre a performance da consulta e a perda mínima de recall. Normalmente, esse valor é inferior a 1 ou 2%.
    • Prévia AH: o hash assimétrico (AH) é até quatro vezes mais compactado em comparação com SQ8. Para um desempenho de consulta potencialmente melhor quando o mecanismo colunar está ativado e os dados de índice e tabela são preenchidos no mecanismo colunar, considere isso. Para mais informações, consulte Práticas recomendadas para ajuste do ScaNN.
    • FLAT: oferece o maior recall, 99% ou mais, ao custo da performance de 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.

Í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. 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 do 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 a performance da consulta e a perda mínima de recall. Normalmente, esse valor é inferior a 1 ou 2%.
    • Prévia AH: o hash assimétrico (AH) é até quatro vezes mais compactado em comparação com SQ8. Para um desempenho de consulta potencialmente melhor quando o mecanismo colunar está ativado e os dados de índice e tabela são preenchidos no mecanismo colunar, considere isso. Para mais informações, consulte Práticas recomendadas para ajuste do ScaNN.
    • FLAT: oferece o maior recall, 99% ou mais, ao custo da performance de 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.

Índice de árvore de quatro níveis

Os índices de árvore de quatro níveis estão em Prévia.

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      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 do 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 a performance da consulta e a perda mínima de recall. Normalmente, esse valor é inferior a 1 ou 2%.
    • Prévia AH: o hash assimétrico (AH) é até quatro vezes mais compactado em comparação com SQ8. Para um desempenho de consulta potencialmente melhor quando o mecanismo colunar está ativado e os dados de índice e tabela são preenchidos no mecanismo colunar, considere isso. Para mais informações, consulte Práticas recomendadas para ajuste do ScaNN.
    • FLAT: oferece o maior recall, 99% ou mais, ao custo da performance de pesquisa.

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 seu í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 do 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 do ScaNN em uma tabela vazia ou com menos linhas do que o valor da opção de índice num_leaves.

Para contornar essa limitação, ative a criação adiada de índices para permitir que o AlloyDB adie a criação de índices até que o número de linhas na tabela atinja o limite definido por num_leaves. Quando 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 recriação do índice acontece em segundo plano, a criação 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 adiada de índices

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

  1. Ative a flag scann.enable_index_maintenance (ativada por padrão) e a flag scann.enable_preview_features. A flag scann.enable_preview_features também ativa outros recursos de visualização.

    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
    

    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 do ScaNN. Se você criar um índice no modo manual, 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 índice usa valores de flags 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 criar um índice do ScaNN.

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

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

  • O índice do ScaNN é treinado com dados insuficientes. Isso pode resultar em baixa capacidade de recall para pesquisas de similaridade de vetores.

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

Recomendamos que você atrase a criação de índices em caso de 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 forçar a criação de índices. A criação forçada de índices exige privilégios de SUPERUSER.

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

  1. Defina o parâmetro scann.allow_blocked_operations creation 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 de 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 seu índice mais rápido, 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 do 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 distância L2. Esses operadores podem usar o índice do 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 esta consulta. Escolha o equivalente de consulta para a 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 a melhor correspondência.

A seguir