Faça a gestão dos índices vetoriais

Este documento descreve como criar e gerir índices vetoriais para acelerar as suas pesquisas vetoriais.

Um índice vetorial é uma estrutura de dados concebida para permitir que a função VECTOR_SEARCH seja executada de forma mais eficiente, especialmente em grandes conjuntos de dados. Quando usa um índice, o VECTOR_SEARCH usa algoritmos de vizinho mais próximo aproximado (ANN) para reduzir a latência das consultas e o custo computacional. Embora a RAA introduza um grau de aproximação, o que significa que a relembrança pode não ser de 100%, as melhorias de desempenho oferecem normalmente uma vantagem para a maioria das aplicações.

Funções e permissões

Para criar um índice vetorial, precisa da bigquery.tables.createIndexautorização de IAM na tabela onde está a criar o índice. Para eliminar um índice vetorial, precisa da autorização bigquery.tables.deleteIndex. Cada uma das seguintes funções de IAM predefinidas inclui as autorizações de que precisa para trabalhar com índices de vetores:

  • Proprietário de dados do BigQuery (roles/bigquery.dataOwner)
  • Editor de dados do BigQuery (roles/bigquery.dataEditor)

Escolha um tipo de índice vetorial

O BigQuery oferece dois tipos de índice vetorial, IVF e TreeAH, cada um deles a suportar diferentes exemplos de utilização. O BigQuery suporta o processamento em lote para a pesquisa vetorial através do processamento de várias linhas dos dados de entrada na função VECTOR_SEARCH. Para pequenos lotes de consultas, os índices IVF são preferíveis. Para grandes lotes de consultas, os índices TreeAH, criados com o algoritmo ScaNN da Google, são preferíveis.

IVF Index

O IVF é um índice de ficheiros invertidos que usa um algoritmo k-means para agrupar os dados vetoriais e, em seguida, divide os dados vetoriais com base nesses grupos. A VECTOR_SEARCH função pode usar estas partições para reduzir a quantidade de dados que precisa de ler para determinar um resultado.

TreeAH Index

O tipo de índice TreeAH tem o nome devido à combinação de uma estrutura semelhante a uma árvore e à utilização de hashing assimétrico (AH), uma técnica de quantização essencial do algoritmo ScaNN subjacente. Um índice TreeAH funciona da seguinte forma:

  1. A tabela base é dividida em fragmentos mais pequenos e mais fáceis de gerir.
  2. É preparado um modelo de clustering com o número de clusters derivado da opção leaf_node_embedding_count no argumento tree_ah_options da declaração CREATE VECTOR INDEX.
  3. Os vetores são comprimidos com a quantização de produtos, uma técnica que reduz a respetiva utilização de memória. Os vetores comprimidos são, em seguida, armazenados nas tabelas de índice em vez dos vetores originais, o que reduz os tamanhos dos índices de vetores.
  4. Quando a função VECTOR_SEARCH é executada, é calculada de forma eficiente uma lista de candidatos para cada vetor de consulta usando a hash assimétrica, que é otimizada por hardware para cálculos de distância aproximados. Estes candidatos são, em seguida, reclassificados e reordenados com base em incorporações exatas.

O algoritmo TreeAH está otimizado para consultas em lote que processam centenas ou mais vetores de consulta. A utilização da quantização de produtos pode reduzir significativamente a latência e o custo, potencialmente em ordens de magnitude em comparação com o IVF. No entanto, devido ao aumento dos custos gerais, o algoritmo IVF pode ser melhor quando tem um número menor de vetores de consulta.

Sugerimos que experimente o tipo de índice TreeAH se o seu exemplo de utilização cumprir os seguintes critérios:

  • A sua tabela contém 200 milhões de linhas ou menos.

  • Executa frequentemente consultas em lote grandes que envolvem centenas ou mais vetores de consulta.

Para consultas de pequenos lotes com o tipo de índice TreeAH, VECTOR_SEARCH pode reverter para pesquisa de força bruta. Quando isto ocorre, é fornecido um IndexUnusedReason para explicar por que motivo o índice de vetores não foi usado.

Crie um índice vetorial IVF

Para criar um índice vetorial IVF, use a declaração de linguagem de definição de dados (LDD): CREATE VECTOR INDEX

  1. Aceda à página do BigQuery.

    Aceda ao BigQuery

  2. No editor de consultas, execute a seguinte declaração SQL:

    Para criar um índice vetorial IVF:

    CREATE [ OR REPLACE ] VECTOR INDEX [ IF NOT EXISTS ] INDEX_NAME
    ON DATASET_NAME.TABLE_NAME(COLUMN_NAME)
    STORING(STORED_COLUMN_NAME [, ...])
    OPTIONS(index_type = 'IVF',
      distance_type = 'DISTANCE_TYPE',
      ivf_options = '{"num_lists":NUM_LISTS}')

    Substitua o seguinte:

    • INDEX_NAME: o nome do índice vetorial que está a criar. Uma vez que o índice é sempre criado no mesmo projeto e conjunto de dados que a tabela base, não é necessário especificá-los no nome.
    • DATASET_NAME: o nome do conjunto de dados que contém a tabela.
    • TABLE_NAME: o nome da tabela que contém a coluna com dados de incorporações.
    • COLUMN_NAME: o nome de uma coluna que contém os dados de incorporações. A coluna tem de ter o tipo ARRAY<FLOAT64>. A coluna não pode ter campos secundários. Todos os elementos na matriz têm de ser nãoNULL, e todos os valores na coluna têm de ter as mesmas dimensões da matriz.
    • STORED_COLUMN_NAME: o nome de uma coluna de nível superior na tabela para armazenar no índice de vetores. O tipo de coluna não pode ser RANGE. As colunas armazenadas não são usadas se a tabela tiver uma política de acesso ao nível da linha ou a coluna tiver uma etiqueta de política. Para informações sobre como ativar colunas armazenadas, consulte Armazene colunas e pré-filtre.
    • DISTANCE_TYPE: especifica o tipo de distância predefinido a usar quando efetuar uma pesquisa vetorial com este índice. Os valores suportados são EUCLIDEAN, COSINE e DOT_PRODUCT. EUCLIDEAN é a predefinição.

      A criação do índice usa sempre a distância EUCLIDEAN para a preparação, mas a distância usada na função VECTOR_SEARCH pode ser diferente.

      Se especificar um valor para o argumento distance_type da função VECTOR_SEARCH, esse valor é usado em vez do valor DISTANCE_TYPE.

    • NUM_LISTS: um valor INT64 que especifica o número de listas que o índice IVF agrupa e, em seguida, divide os dados vetoriais. Este valor tem de ser igual ou inferior a 5000. Durante a indexação, são atribuídos vetores à lista correspondente ao centroide do cluster mais próximo. Se omitir este argumento, o BigQuery determina um valor predefinido com base nas características dos seus dados. O valor predefinido funciona bem para a maioria dos exemplos de utilização.

      NUM_LISTS controla o nível de detalhe do ajuste das consultas. Os valores mais elevados criam mais listas, pelo que pode definir a opção fraction_lists_to_search da função VECTOR_SEARCH para analisar uma percentagem menor do índice. Por exemplo, analisar 1% de 100 listas em vez de analisar 10% de 10 listas. Isto permite um controlo mais preciso da velocidade de pesquisa e da capacidade de memorização, mas aumenta ligeiramente o custo de indexação. Defina este valor do argumento com base na precisão com que precisa de ajustar o âmbito da consulta.

O exemplo seguinte cria um índice vetorial na coluna embedding de my_table:

CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>);

CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
OPTIONS(index_type = 'IVF');

O exemplo seguinte cria um índice vetorial na coluna embedding de my_table e especifica o tipo de distância a usar e as opções de IVF:

CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>);

CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
OPTIONS(index_type = 'IVF', distance_type = 'COSINE',
ivf_options = '{"num_lists": 2500}')

Crie um índice vetorial TreeAH

Para criar um índice vetorial TreeAH, use a declaração de linguagem de definição de dados (LDD):CREATE VECTOR INDEX

  1. Aceda à página do BigQuery.

    Aceda ao BigQuery

  2. No editor de consultas, execute a seguinte declaração SQL:

    CREATE [ OR REPLACE ] VECTOR INDEX [ IF NOT EXISTS ] INDEX_NAME
    ON DATASET_NAME.TABLE_NAME(COLUMN_NAME)
    STORING(STORED_COLUMN_NAME [, ...])
    OPTIONS(index_type = 'TREE_AH',
      distance_type = 'DISTANCE_TYPE',
      tree_ah_options = '{"leaf_node_embedding_count":LEAF_NODE_EMBEDDING_COUNT,
        "normalization_type":"NORMALIZATION_TYPE"}')

    Substitua o seguinte:

    • INDEX_NAME: o nome do índice vetorial que está a criar. Uma vez que o índice é sempre criado no mesmo projeto e conjunto de dados que a tabela base, não é necessário especificá-los no nome.
    • DATASET_NAME: o nome do conjunto de dados que contém a tabela.
    • TABLE_NAME: o nome da tabela que contém a coluna com dados de incorporações.
    • COLUMN_NAME: o nome de uma coluna que contém os dados de incorporações. A coluna tem de ter o tipo ARRAY<FLOAT64>. A coluna não pode ter campos secundários. Todos os elementos na matriz têm de ser nãoNULL, e todos os valores na coluna têm de ter as mesmas dimensões da matriz.
    • STORED_COLUMN_NAME: o nome de uma coluna de nível superior na tabela para armazenar no índice de vetores. O tipo de coluna não pode ser RANGE. As colunas armazenadas não são usadas se a tabela tiver uma política de acesso ao nível da linha ou a coluna tiver uma etiqueta de política. Para informações sobre como ativar colunas armazenadas, consulte Armazene colunas e pré-filtre.
    • DISTANCE_TYPE: um argumento opcional que especifica o tipo de distância predefinido a usar quando efetuar uma pesquisa vetorial com este índice. Os valores suportados são EUCLIDEAN, COSINE e DOT_PRODUCT. EUCLIDEAN é a predefinição.

      A criação do índice usa sempre a distância EUCLIDEAN para a preparação, mas a distância usada na função VECTOR_SEARCH pode ser diferente.

      Se especificar um valor para o argumento distance_type da função VECTOR_SEARCH, esse valor é usado em vez do valor DISTANCE_TYPE.

    • LEAF_NODE_EMBEDDING_COUNT: um valor INT64 superior ou igual a 500 que especifica o número aproximado de vetores em cada nó folha da árvore que o algoritmo TreeAH cria. O algoritmo TreeAH divide todo o espaço de dados num número de listas, com cada lista a conter aproximadamente LEAF_NODE_EMBEDDING_COUNT pontos de dados. Um valor inferior cria mais listas com menos pontos de dados, enquanto um valor superior cria menos listas com mais pontos de dados. A predefinição é 1000, o que é adequado para a maioria dos conjuntos de dados.

    • NORMALIZATION_TYPE: um valor de STRING. Os valores suportados são NONE ou L2. A predefinição é NONE. A normalização ocorre antes de qualquer processamento, tanto para os dados da tabela base como para os dados de consulta, mas não modifica a coluna de incorporação COLUMN_NAME em TABLE_NAME. Consoante o conjunto de dados, o modelo de incorporação e o tipo de distância usados durante a VECTOR_SEARCH, a normalização das incorporações pode melhorar a capacidade de memorização.

O exemplo seguinte cria um índice vetorial na coluna embedding de my_table e especifica o tipo de distância a usar e as opções TreeAH:

CREATE TABLE my_dataset.my_table(id INT64, embedding ARRAY<FLOAT64>);

CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
OPTIONS (index_type = 'TREE_AH', distance_type = 'EUCLIDEAN',
tree_ah_options = '{"normalization_type": "L2"}');

Filtragem

As secções seguintes explicam como os pré-filtros e os pós-filtros afetam os resultados da pesquisa vetorial, bem como a forma de pré-filtrar através da utilização de colunas armazenadas e partições no índice vetorial.

Pré-filtros e pós-filtros

Nas operações do BigQuery, a filtragem prévia e a filtragem posterior servem para refinar os resultados da pesquisa, aplicando condições com base nas colunas de metadados associadas às incorporações de vetores.VECTOR_SEARCH É importante compreender as diferenças, a implementação e o impacto para otimizar o desempenho, o custo e a precisão das consultas.

A pré-filtragem e a pós-filtragem são definidas da seguinte forma:

  • Pré-filtragem: aplica condições de filtro antes de a pesquisa do vizinho mais próximo aproximado (ANN) realizar cálculos de distância em vetores candidatos. Isto restringe o conjunto de vetores que são considerados durante a pesquisa. Consequentemente, a pré-filtragem resulta frequentemente em tempos de consulta mais rápidos e num custo computacional reduzido, uma vez que a pesquisa de ANN avalia menos potenciais candidatos.
  • Pós-filtragem: aplica condições de filtro após a identificação dos top_k vizinhos mais próximos iniciais pela pesquisa ANN. Isto refina o conjunto de resultados final com base nos critérios especificados.

O posicionamento da cláusula WHERE determina se um filtro funciona como um pré-filtro ou um pós-filtro.

Para criar um pré-filtro, a cláusula WHERE da consulta tem de se aplicar ao argumento da tabela base da função VECTOR_SEARCH. O predicado tem de se aplicar a uma coluna armazenada. Caso contrário, torna-se efetivamente um filtro posterior.

O exemplo seguinte mostra como criar um pré-filtro:

-- Pre-filter on a stored column. The index speeds up the query.
SELECT *
FROM
  VECTOR_SEARCH(
    (SELECT * FROM my_dataset.my_table WHERE type = 'animal'),
    'embedding',
    TABLE my_dataset.my_testdata);

-- Filter on a column that isn't stored. The index is used to search the
-- entire table, and then the results are post-filtered. You might see fewer
-- than 5 matches returned for some embeddings.
SELECT query.test_id, base.type, distance
FROM
  VECTOR_SEARCH(
    (SELECT * FROM my_dataset.my_table WHERE id = 123),
    'embedding',
    TABLE my_dataset.my_testdata,
    top_k => 5);

-- Use pre-filters with brute force. The data is filtered and then searched
-- with brute force for exact results.
SELECT query.test_id, base.type, distance
FROM
  VECTOR_SEARCH(
    (SELECT * FROM my_dataset.my_table WHERE id = 123),
    'embedding',
    TABLE my_dataset.my_testdata,
    options => '{"use_brute_force":true}');

Para criar um filtro posterior, a cláusula WHERE da consulta tem de ser aplicada fora da função VECTOR_SEARCH, para que filtre os resultados devolvidos pela pesquisa.

O exemplo seguinte mostra como criar um filtro posterior:

-- Use post-filters. The index is used, but the entire table is searched and
-- the post-filtering might reduce the number of results.
SELECT query.test_id, base.type, distance
FROM
  VECTOR_SEARCH(
    TABLE my_dataset.my_table,
    'embedding',
    TABLE my_dataset.my_testdata,
    top_k => 5)
WHERE base.type = 'animal';

SELECT base.id, distance
FROM
  VECTOR_SEARCH(
    TABLE mydataset.base_table,
    'embedding',
    (SELECT embedding FROM mydataset.query_table),
    top_k => 10
  )
WHERE type = 'document' AND year > 2022

Quando usa a filtragem posterior ou quando os filtros da tabela base especificados fazem referência a colunas não armazenadas e, por isso, atuam como filtros posteriores, o conjunto de resultados final pode conter menos de top_k linhas, potencialmente até zero linhas, se o predicado for seletivo. Se precisar de um número específico de resultados após a filtragem, considere especificar um valor top_k maior ou aumentar o valor fraction_lists_to_search na chamada VECTOR_SEARCH.

Em alguns casos, especialmente se o pré-filtro for muito seletivo, a pré-filtragem também pode reduzir o tamanho do conjunto de resultados. Se isto acontecer, experimente aumentar o valor de fraction_lists_to_search na chamada VECTOR_SEARCH.

Pré-filtre com colunas armazenadas

Para melhorar ainda mais a eficiência do seu índice vetorial, pode especificar colunas da tabela base para armazenar no índice vetorial. A utilização de colunas armazenadas pode otimizar as consultas que chamam a função VECTOR_SEARCH das seguintes formas:

  • Em vez de pesquisar numa tabela inteira, pode chamar a função VECTOR_SEARCH numa declaração de consulta que pré-filtra a tabela base com uma cláusula WHERE. Se a sua tabela tiver um índice e filtrar apenas colunas armazenadas, o BigQuery otimiza a consulta filtrando os dados antes da pesquisa e, em seguida, usando o índice para pesquisar o conjunto de resultados mais pequeno. Se filtrar por colunas que não estão armazenadas, o BigQuery aplica o filtro após a pesquisa da tabela ou pós-filtros.

  • A função VECTOR_SEARCH gera uma struct denominada base que contém todas as colunas da tabela base. Sem colunas armazenadas, é necessária uma junção potencialmente cara para obter as colunas armazenadas em base. Se usar um índice IVF e a sua consulta selecionar apenas colunas armazenadas de base, o BigQuery otimiza a consulta para eliminar essa junção. Para os índices TreeAH, a junção com a tabela base não é removida. As colunas armazenadas nos índices TreeAH são usadas apenas para fins de pré-filtragem.

Para armazenar colunas, liste-as na cláusula STORING da declaração CREATE VECTOR INDEX. O armazenamento de colunas aumenta o tamanho do índice vetorial, pelo que é melhor armazenar apenas as colunas usadas ou filtradas com maior frequência.

O exemplo seguinte cria um índice vetorial com colunas armazenadas e, em seguida, executa uma consulta de pesquisa vetorial que seleciona apenas colunas armazenadas:

-- Create a table that contains an embedding.
CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>, type STRING, creation_time DATETIME, id INT64);

-- Create a query table that contains an embedding.
CREATE TABLE my_dataset.my_testdata(embedding ARRAY<FLOAT64>, test_id INT64);

-- Create a vector index with stored columns.
CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
STORING (type, creation_time)
OPTIONS (index_type = 'IVF');

-- Select only stored columns from a vector search to avoid an expensive join.
SELECT query, base.type, distance
FROM
  VECTOR_SEARCH(
    TABLE my_dataset.my_table,
    'embedding'
    TABLE my_dataset.my_testdata);

Limitações de colunas armazenadas

  • Se o modo, o tipo ou o esquema de uma coluna for alterado na tabela base e se for uma coluna armazenada no índice vetorial, pode haver um atraso antes de essa alteração ser refletida no índice vetorial. Até que as atualizações sejam aplicadas ao índice, as consultas de pesquisa vetorial usam as colunas armazenadas modificadas da tabela base.
  • Se selecionar uma coluna do tipo STRUCT na saída de uma consulta VECTOR_SEARCH numa tabela que tenha um índice com colunas armazenadas, toda a consulta pode falhar.query

Pré-filtragem com partições

Se a tabela na qual está a criar o índice de vetores estiver particionada, pode optar por particionar também o índice de vetores. A partição do índice de vetores tem as seguintes vantagens:

  • A eliminação de partições é aplicada aos índices vetoriais, além das partições da tabela. A eliminação de partições ocorre quando a pesquisa vetorial usa um filtro de qualificação no valor da coluna de particionamento. Isto permite ao BigQuery analisar as partições que correspondem ao filtro e ignorar as partições restantes. O corte de partições pode diminuir os custos de E/S. Para mais informações sobre a eliminação de partições, consulte o artigo Consultar tabelas particionadas.
  • É menos provável que a pesquisa vetorial perca resultados se pré-filtrar na coluna de particionamento.

Só pode particionar índices vetoriais TreeAH.

A partição de um índice vetorial só é recomendada se usar a pré-filtragem para limitar a maioria das suas pesquisas vetoriais a algumas partições.

Para criar um índice particionado, use a cláusula PARTITION BY da declaração CREATE VECTOR INDEX. A cláusula PARTITION BY que especificar na declaração CREATE VECTOR INDEX tem de ser igual à cláusula PARTITION BY especificada na declaração CREATE TABLE da tabela na qual está a criar o índice vetorial, conforme mostrado no exemplo seguinte:

-- Create a date-partitioned table.
CREATE TABLE my_dataset.my_table(
  embeddings ARRAY
  id INT64,
  date DATE,
)
PARTITION BY date;

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX my_index ON my_dataset.my_table(embeddings)
PARTITION BY date
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Se a tabela usar a partição de colunas de intervalo de números inteiros ou de unidades de tempo, a coluna de partição é armazenada no índice vetorial, o que aumenta o custo de armazenamento. Se uma coluna de tabela for usada nas cláusulas STORING e PARTITION BY da declaração CREATE VECTOR INDEX, a coluna é armazenada apenas uma vez.

Para usar a partição do índice vetorial, filtre a coluna de partição na subconsulta da tabela base da declaração VECTOR_SEARCH. No exemplo seguinte, a tabela samples.items é particionada pela coluna produced_date, pelo que a subconsulta da tabela base na declaração VECTOR_SEARCH filtra a coluna produced_date:

SELECT query.id, base.id, distance
FROM VECTOR_SEARCH(
  (SELECT * FROM my_dataset.my_table WHERE produced_date = '2025-01-01'),
  'embedding',
  TABLE samples.test,
  distance_type => 'COSINE',
  top_k => 10
);

Exemplos

Crie um índice vetorial particionado numa tabela particionada por data/hora:

-- Create a datetime-partitioned table.
CREATE TABLE my_dataset.my_table(
  id INT64,
  produced_date DATETIME,
  embeddings ARRAY
)
PARTITION BY produced_date;

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX index0 ON my_dataset.my_table(embeddings)
PARTITION BY produced_date
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Crie um índice vetorial particionado numa tabela particionada por data/hora:

-- Create a timestamp-partitioned table.
CREATE TABLE my_dataset.my_table(
  id INT64,
  produced_time TIMESTAMP,
  embeddings ARRAY
)
PARTITION BY TIMESTAMP_TRUNC(produced_time, HOUR);

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX index0 ON my_dataset.my_table(embeddings)
PARTITION BY TIMESTAMP_TRUNC(produced_time, HOUR)
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Crie um índice de vetores particionado numa tabela particionada por intervalo de números inteiros:

-- Create a integer range-partitioned table.
CREATE TABLE my_dataset.my_table(
  id INT64,
  embeddings ARRAY
)
PARTITION BY RANGE_BUCKET(id, GENERATE_ARRAY(-100, 100, 20));

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX index0 ON my_dataset.my_table(embeddings)
PARTITION BY RANGE_BUCKET(id, GENERATE_ARRAY(-100, 100, 20))
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Crie um índice de vetores particionado numa tabela particionada por tempo de ingestão:

-- Create a ingestion time-partitioned table.
CREATE TABLE my_dataset.my_table(
  id INT64,
  embeddings ARRAY
)
PARTITION BY TIMESTAMP_TRUNC(_PARTITIONTIME, DAY);

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX index0 ON my_dataset.my_table(embeddings)
PARTITION BY TIMESTAMP_TRUNC(_PARTITIONTIME, DAY)
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Limitações da pré-filtragem

  • Não pode usar vistas lógicas no pré-filtro.
  • Se o pré-filtro contiver uma subconsulta, pode interferir com a utilização do índice.

Compreender quando os dados são indexados

Os índices vetoriais são totalmente geridos pelo BigQuery e são atualizados automaticamente quando a tabela indexada é alterada.

A indexação é assíncrona. Existe um atraso entre a adição de novas linhas à tabela base e a apresentação das novas linhas no índice. No entanto, a função VECTOR_SEARCH continua a ter em conta todas as linhas e não perde linhas não indexadas. A função pesquisa através do índice os registos indexados e usa a pesquisa de força bruta para os registos que ainda não estão indexados.

Se criar um índice vetorial numa tabela com menos de 10 MB, o índice vetorial não é preenchido. Da mesma forma, se eliminar dados de uma tabela indexada e o tamanho da tabela ficar abaixo de 10 MB, o índice de vetores é desativado temporariamente. Neste caso, as consultas de pesquisa vetorial não usam o índice e o código indexUnusedReasons na secção vectorSearchStatistics do recurso Job é BASE_TABLE_TOO_SMALL. Sem o índice, VECTOR_SEARCH recorre automaticamente à utilização da força bruta para encontrar os vizinhos mais próximos das incorporações.

Se eliminar a coluna indexada numa tabela ou mudar o nome da própria tabela, o índice de vetores é eliminado automaticamente.

Monitorizar o estado dos índices vetoriais

Pode monitorizar o estado dos seus índices vetoriais consultando as visualizações INFORMATION_SCHEMA. As seguintes vistas contêm metadados sobre índices vetoriais:

  • A vista INFORMATION_SCHEMA.VECTOR_INDEXES tem informações sobre os índices vetoriais num conjunto de dados.

    Após a conclusão da declaração CREATE VECTOR INDEX, o índice tem de ser preenchido antes de o poder usar. Pode usar as colunas last_refresh_time e coverage_percentage para verificar a disponibilidade de um índice de vetores. Se o índice vetorial não estiver pronto, pode continuar a usar a função VECTOR_SEARCH numa tabela, mas pode ser executada mais lentamente sem o índice.

  • A vista INFORMATION_SCHEMA.VECTOR_INDEX_COLUMNS tem informações sobre as colunas indexadas por vetores para todas as tabelas num conjunto de dados.

  • A INFORMATION_SCHEMA.VECTOR_INDEX_OPTIONS vista tem informações sobre as opções usadas pelos índices vetoriais num conjunto de dados.

Exemplos de índices vetoriais

O exemplo seguinte mostra todos os índices vetoriais ativos em tabelas no conjunto de dados my_dataset, localizados no projeto my_project. Inclui os respetivos nomes, as declarações DDL usadas para os criar e a percentagem de cobertura. Se uma tabela base indexada tiver menos de 10 MB, o respetivo índice não é preenchido. Nesse caso, o valor coverage_percentage é 0.

SELECT table_name, index_name, ddl, coverage_percentage
FROM my_project.my_dataset.INFORMATION_SCHEMA.VECTOR_INDEXES
WHERE index_status = 'ACTIVE';

O resultado é semelhante ao seguinte:

+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+
| table_name | index_name | ddl                                                                                             | coverage_percentage |
+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+
| table1     | indexa     | CREATE VECTOR INDEX `indexa` ON `my_project.my_dataset.table1`(embeddings)                      | 100                 |
|            |            | OPTIONS (distance_type = 'EUCLIDEAN', index_type = 'IVF', ivf_options = '{"num_lists": 100}')   |                     |
+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+
| table2     | indexb     | CREATE VECTOR INDEX `indexb` ON `my_project.my_dataset.table2`(vectors)                         | 42                  |
|            |            | OPTIONS (distance_type = 'COSINE', index_type = 'IVF', ivf_options = '{"num_lists": 500}')      |                     |
+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+
| table3     | indexc     | CREATE VECTOR INDEX `indexc` ON `my_project.my_dataset.table3`(vectors)                         | 98                  |
|            |            | OPTIONS (distance_type = 'DOT_PRODUCT', index_type = 'TREE_AH',                                 |                     |
|            |            |          tree_ah_options = '{"leaf_node_embedding_count": 1000, "normalization_type": "NONE"}') |                     |
+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+

Exemplos de colunas de índice vetorial

A seguinte consulta extrai informações sobre colunas que têm índices vetoriais:

SELECT table_name, index_name, index_column_name, index_field_path
FROM my_project.dataset.INFORMATION_SCHEMA.VECTOR_INDEX_COLUMNS;

O resultado é semelhante ao seguinte:

+------------+------------+-------------------+------------------+
| table_name | index_name | index_column_name | index_field_path |
+------------+------------+-------------------+------------------+
| table1     | indexa     | embeddings        | embeddings       |
| table2     | indexb     | vectors           | vectors          |
| table3     | indexc     | vectors           | vectors          |
+------------+------------+-------------------+------------------+

Exemplos de opções de índice vetorial

A seguinte consulta extrai informações sobre as opções do índice vetorial:

SELECT table_name, index_name, option_name, option_type, option_value
FROM my_project.dataset.INFORMATION_SCHEMA.VECTOR_INDEX_OPTIONS;

O resultado é semelhante ao seguinte:

+------------+------------+------------------+------------------+-------------------------------------------------------------------+
| table_name | index_name | option_name      | option_type      | option_value                                                      |
+------------+------------+------------------+------------------+-------------------------------------------------------------------+
| table1     | indexa     | index_type       | STRING           | IVF                                                               |
| table1     | indexa     | distance_type    | STRING           | EUCLIDEAN                                                         |
| table1     | indexa     | ivf_options      | STRING           | {"num_lists": 100}                                                |
| table2     | indexb     | index_type       | STRING           | IVF                                                               |
| table2     | indexb     | distance_type    | STRING           | COSINE                                                            |
| table2     | indexb     | ivf_options      | STRING           | {"num_lists": 500}                                                |
| table3     | indexc     | index_type       | STRING           | TREE_AH                                                           |
| table3     | indexc     | distance_type    | STRING           | DOT_PRODUCT                                                       |
| table3     | indexc     | tree_ah_options  | STRING           | {"leaf_node_embedding_count": 1000, "normalization_type": "NONE"} |
+------------+------------+------------------+------------------+-------------------------------------------------------------------+

Validar a utilização do índice vetorial

As informações sobre a utilização do índice vetorial estão disponíveis nos metadados da tarefa que executou a consulta de pesquisa vetorial. Pode ver os metadados das tarefas através da consola, da ferramenta de linha de comandos bq, da API BigQuery ou das bibliotecas de cliente. Trusted Cloud

Quando usa a Trusted Cloud consola, pode encontrar informações de utilização do índice vetorial nos campos Modo de utilização do índice vetorial e Motivos de não utilização do índice vetorial.

Quando usa a ferramenta bq ou a API BigQuery, pode encontrar informações de utilização do índice de vetores na secção VectorSearchStatistics do recurso Job.

O modo de utilização do índice indica se foi usado um índice vetorial, fornecendo um dos seguintes valores:

  • UNUSED: não foi usado nenhum índice vetorial.
  • PARTIALLY_USED: algumas funções VECTOR_SEARCH na consulta usaram índices de vetores e outras não.
  • FULLY_USED: Todas as funções VECTOR_SEARCH na consulta usaram um índice vetorial.

Quando o valor do modo de utilização do índice é UNUSED ou PARTIALLY_USED, os motivos de não utilização do índice indicam por que motivo os índices de vetores não foram usados na consulta.

Por exemplo, os seguintes resultados devolvidos por bq show --format=prettyjson -j my_job_id mostram que o índice não foi usado porque a opção use_brute_force foi especificada na função VECTOR_SEARCH:

"vectorSearchStatistics": {
  "indexUnusedReasons": [
    {
      "baseTable": {
        "datasetId": "my_dataset",
        "projectId": "my_project",
        "tableId": "my_table"
      },
      "code": "INDEX_SUPPRESSED_BY_FUNCTION_OPTION",
      "message": "No vector index was used for the base table `my_project:my_dataset.my_table` because use_brute_force option has been specified."
    }
  ],
  "indexUsageMode": "UNUSED"
}

Opções de gestão de índices

Para criar índices e fazer com que o BigQuery os mantenha, tem duas opções:

  • Use o conjunto de slots partilhados predefinido: quando os dados que planeia indexar estão abaixo do seu limite por organização, pode usar o conjunto de slots partilhados gratuito para a gestão do índice.
  • Use a sua própria reserva: para alcançar um progresso de indexação mais previsível e consistente nas suas cargas de trabalho de produção maiores, pode usar as suas próprias reservas para a gestão de índices.

Use espaços partilhados

Se não tiver configurado o seu projeto para usar uma reserva dedicada para indexação, a gestão de índices é processada no conjunto de slots partilhados gratuito, sujeita às seguintes restrições.

Se adicionar dados a uma tabela que façam com que o tamanho total das tabelas indexadas exceda o limite da sua organização, o BigQuery pausa a gestão de índices para essa tabela. Quando isto acontece, o campo index_status na vista INFORMATION_SCHEMA.VECTOR_INDEXES apresenta PENDING DISABLEMENT e o índice é colocado em fila para eliminação. Enquanto o índice estiver pendente de desativação, continua a ser usado em consultas, e é-lhe cobrado o armazenamento do índice. Após a eliminação do índice, o campo index_status mostra o índice como TEMPORARILY DISABLED. Neste estado, as consultas não usam o índice e não lhe é cobrado o armazenamento do índice. Neste caso, o código IndexUnusedReason é BASE_TABLE_TOO_LARGE.

Se eliminar dados da tabela e o tamanho total das tabelas indexadas ficar abaixo do limite por organização, a gestão de índices é retomada. Se o campo index_status na vista INFORMATION_SCHEMA.VECTOR_INDEXES for ACTIVE, as consultas podem usar o índice e é-lhe cobrado o armazenamento do índice.

Pode usar a vista INFORMATION_SCHEMA.SEARCH_INDEXES_BY_ORGANIZATION para compreender o seu consumo atual em relação ao limite por organização numa determinada região, discriminado por projetos e tabelas.

O BigQuery não oferece garantias sobre a capacidade disponível do conjunto partilhado nem sobre o débito de indexação que vê. Para aplicações de produção, recomendamos que use ranhuras dedicadas para o processamento do índice.

Use a sua própria reserva

Em vez de usar o conjunto de intervalos partilhados predefinido, pode designar opcionalmente a sua própria reserva para indexar as tabelas. A utilização da sua própria reserva garante um desempenho previsível e consistente das tarefas de gestão de índices, como a criação, a atualização e as otimizações em segundo plano.

  • Não existem limites de tamanho da tabela quando uma tarefa de indexação é executada na sua reserva.
  • A utilização da sua própria reserva dá-lhe flexibilidade na gestão do índice. Se precisar de criar um índice muito grande ou fazer uma atualização importante a uma tabela indexada, pode adicionar temporariamente mais espaços à atribuição.

Para indexar as tabelas num projeto com uma reserva designada, crie uma reserva na região onde as tabelas estão localizadas. Em seguida, atribua o projeto à reserva com o valor job_type definido como BACKGROUND:

SQL

Use a CREATE ASSIGNMENT declaração DDL.

  1. Na Trusted Cloud consola, aceda à página BigQuery.

    Aceda ao BigQuery

  2. No editor de consultas, introduza a seguinte declaração:

    CREATE ASSIGNMENT
      `ADMIN_PROJECT_ID.region-LOCATION.RESERVATION_NAME.ASSIGNMENT_ID`
    OPTIONS (
      assignee = 'projects/PROJECT_ID',
      job_type = 'BACKGROUND');

    Substitua o seguinte:

    • ADMIN_PROJECT_ID: o ID do projeto do projeto de administração que detém o recurso de reserva
    • LOCATION: a localização da reserva
    • RESERVATION_NAME: o nome da reserva
    • ASSIGNMENT_ID: o ID da atribuição

      O ID tem de ser exclusivo do projeto e da localização, começar e terminar com uma letra minúscula ou um número, e conter apenas letras minúsculas, números e traços.

    • PROJECT_ID: o ID do projeto que contém as tabelas a indexar. Este projeto está atribuído à reserva.

  3. Clique em Executar.

Para mais informações sobre como executar consultas, consulte o artigo Execute uma consulta interativa.

bq

Use o comando bq mk:

bq mk \
    --project_id=ADMIN_PROJECT_ID \
    --location=LOCATION \
    --reservation_assignment \
    --reservation_id=RESERVATION_NAME \
    --assignee_id=PROJECT_ID \
    --job_type=BACKGROUND \
    --assignee_type=PROJECT

Substitua o seguinte:

  • ADMIN_PROJECT_ID: o ID do projeto do projeto de administração que detém o recurso de reserva
  • LOCATION: a localização da reserva
  • RESERVATION_NAME: o nome da reserva
  • PROJECT_ID: o ID do projeto a atribuir a esta reserva

Veja as suas tarefas de indexação

É criado um novo trabalho de indexação sempre que um índice é criado ou atualizado numa única tabela. Para ver informações sobre a tarefa, consulte as vistas INFORMATION_SCHEMA.JOBS*. Pode filtrar tarefas de indexação definindo job_type IS NULL AND SEARCH(job_id, '`search_index`') na cláusula WHERE da sua consulta. O exemplo seguinte apresenta os cinco trabalhos de indexação mais recentes no projeto my_project:

SELECT *
FROM
 region-us.INFORMATION_SCHEMA.JOBS
WHERE
  project_id  = 'my_project'
  AND job_type IS NULL
  AND SEARCH(job_id, '`search_index`')
ORDER BY
 creation_time DESC
LIMIT 5;

Escolha o tamanho da reserva

Para escolher o número certo de vagas para a sua reserva, deve considerar quando os trabalhos de gestão de índices são executados, quantas vagas usam e como é a sua utilização ao longo do tempo. O BigQuery aciona uma tarefa de gestão de índices nas seguintes situações:

  • Cria um índice numa tabela.
  • Os dados são modificados numa tabela indexada.
  • O esquema de uma tabela muda e isto afeta as colunas que são indexadas.
  • Os dados e os metadados do índice são otimizados ou atualizados periodicamente.

O número de ranhuras necessárias para uma tarefa de gestão de índices numa tabela depende dos seguintes fatores:

  • O tamanho da tabela
  • A taxa de carregamento de dados para a tabela
  • A taxa de instruções DML aplicadas à tabela
  • O atraso aceitável para criar e manter o índice
  • A complexidade do índice, normalmente determinada pelos atributos dos dados, como o número de termos duplicados
Monitorize a utilização e o progresso

A melhor forma de avaliar o número de espaços que precisa para executar eficientemente as suas tarefas de gestão de índices é monitorizar a utilização de espaços e ajustar o tamanho da reserva em conformidade. A seguinte consulta produz a utilização diária de slots para tarefas de gestão de índices. Apenas os últimos 30 dias estão incluídos na região us-west1:

SELECT
  TIMESTAMP_TRUNC(job.creation_time, DAY) AS usage_date,
  -- Aggregate total_slots_ms used for index-management jobs in a day and divide
  -- by the number of milliseconds in a day. This value is most accurate for
  -- days with consistent slot usage.
  SAFE_DIVIDE(SUM(job.total_slot_ms), (1000 * 60 * 60 * 24)) AS average_daily_slot_usage
FROM
  `region-us-west1`.INFORMATION_SCHEMA.JOBS job
WHERE
  project_id = 'my_project'
  AND job_type IS NULL
  AND SEARCH(job_id, '`search_index`')
GROUP BY
  usage_date
ORDER BY
  usage_date DESC
limit 30;

Quando existem slots insuficientes para executar tarefas de gestão de índices, um índice pode ficar dessincronizado com a respetiva tabela e as tarefas de indexação podem falhar. Neste caso, o BigQuery recompila o índice de raiz. Para evitar ter um índice dessincronizado, certifique-se de que tem slots suficientes para suportar as atualizações do índice a partir do carregamento e da otimização de dados. Para mais informações sobre a monitorização da utilização de faixas, consulte os gráficos de recursos de administrador.

Reconstrua um índice vetorial

Quando os dados da tabela mudam significativamente após a criação de um índice vetorial, o índice vetorial pode tornar-se menos eficiente. Quando um índice vetorial é menos eficiente, uma consulta de pesquisa vetorial que inicialmente tinha uma relembrança elevada quando usava o índice tem uma relembrança inferior, porque a mudança na distribuição de dados na tabela base não está representada no índice vetorial.

Se quiser melhorar a capacidade de memorização sem aumentar a latência das consultas de pesquisa, recompile o índice de vetores. Em alternativa, pode aumentar o valor da opção fraction_lists_to_search de pesquisa de vetores para melhorar a capacidade de memorização, mas isto normalmente torna a consulta de pesquisa mais lenta.

Pode usar a função VECTOR_INDEX.STATISTICS para calcular a variação dos dados de uma tabela indexada entre o momento em que foi criado um índice de vetores e o presente. Se os dados da tabela tiverem sido alterados o suficiente para exigir uma recompilação do índice de vetores, pode usar a declaração ALTER VECTOR INDEX REBUILD para recompilar o índice de vetores.

Siga estes passos para reconstruir um índice vetorial:

  1. Aceda à página do BigQuery.

    Aceda ao BigQuery

  2. No editor de consultas, execute a seguinte declaração SQL para verificar a variação dos dados da tabela indexada:

    SELECT * FROM VECTOR_INDEX.STATISTICS(TABLE DATASET_NAME.TABLE_NAME);

    Substitua o seguinte:

    • DATASET_NAME: o nome do conjunto de dados que contém a tabela indexada.
    • TABLE_NAME: o nome da tabela que contém o índice vetorial.

    A função devolve um valor FLOAT64 no intervalo [0,1). Um valor mais baixo indica um desvio menor. Normalmente, um valor de 0.3 ou superior é considerado uma alteração suficientemente significativa para indicar que uma recompilação do índice vetorial pode ser benéfica.

  3. Se a função VECTOR_INDEX.STATISTICS indicar que a variação dos dados da tabela é significativa, execute a seguinte declaração SQL para reconstruir o índice vetorial:

    ALTER VECTOR INDEX IF EXISTS INDEX_NAME
    ON DATASET_NAME.TABLE_NAME
    REBUILD;

    Substitua o seguinte:

    • INDEX_NAME: o nome do índice vetorial que está a reconstruir.
    • DATASET_NAME: o nome do conjunto de dados que contém a tabela indexada.
    • TABLE_NAME: o nome da tabela que contém o índice vetorial.

Elimine um índice vetorial

Quando já não precisar de um índice vetorial ou quiser alterar a coluna indexada numa tabela, pode eliminar o índice nessa tabela através da declaração DDL DROP VECTOR INDEX:

  1. Aceda à página do BigQuery.

    Aceda ao BigQuery

  2. No editor de consultas, execute a seguinte declaração SQL:

    DROP VECTOR INDEX INDEX_NAME ON DATASET_NAME.TABLE_NAME;

    Substitua o seguinte:

    • INDEX_NAME: o nome do índice vetorial que está a eliminar.
    • DATASET_NAME: o nome do conjunto de dados que contém a tabela indexada.
    • TABLE_NAME: o nome da tabela que contém o índice vetorial.

Se uma tabela indexada for eliminada, o respetivo índice é eliminado automaticamente.

Para ativar aplicações online de latência ultrabaixa, use a integração do BigQuery com a pesquisa vetorial da Vertex AI para importar as suas incorporações do BigQuery para a pesquisa vetorial e implementar pontos finais de baixa latência. Para mais informações, consulte o artigo Importe dados de índice do BigQuery.

O que se segue?