Esta página explica como criar e gerenciar índices vetoriais do Spanner, que usam a pesquisa de vizinhos mais próximos aproximados (ANN, na sigla em inglês) e estruturas baseadas em árvores para acelerar as pesquisas de similaridade vetorial nos seus dados.
O Spanner acelera as pesquisas de vetores do vizinho mais próximo aproximado (ANN) usando um índice vetorial especializado. Esse índice aproveita o Google Research's Scalable Nearest Neighbor (ScaNN), um algoritmo de vizinho mais próximo altamente eficiente.
O índice vetorial usa uma estrutura baseada em árvores para particionar dados e facilitar pesquisas mais rápidas. O Spanner oferece configurações de árvores de dois e três níveis:
- Configuração de árvore de dois níveis: os nós folha (
num_leaves) contêm grupos de vetores intimamente relacionados, além do centroide correspondente. O nível raiz consiste nos centroides de todos os nós folha. - Configuração de árvore de três níveis: semelhante ao conceito de uma árvore de dois níveis, mas introduzindo uma camada de ramificação adicional (
num_branches), em que os centroides dos nós folha são ainda mais particionados para formar o nível raiz (num_leaves).
O Spanner escolhe um índice para você. No entanto, se você souber que um
índice específico funciona melhor, use a FORCE_INDEX dica
para escolher o índice vetorial mais adequado ao seu caso de uso.
Para mais informações, consulte as instruções
VECTOR INDEX para GoogleSQL
e INDEX para PostgreSQL.
Limitações
- Não é possível pré-dividir índices vetoriais. Para mais informações, consulte Visão geral da pré-divisão.
Criar índice vetorial
Para otimizar o recall e a performance de um índice vetorial, recomendamos que você:
Crie o índice vetorial depois que a maioria das linhas com embeddings forem gravadas no banco de dados. Talvez também seja necessário recriar o índice vetorial periodicamente depois de inserir novos dados. Para mais informações, consulte Recriar o índice vetorial.
Para o GoogleSQL, use a cláusula
STORINGe, para o PostgreSQL, use a cláusulaINCLUDEpara armazenar uma cópia de uma coluna no índice vetorial. Se um valor de coluna for armazenado no índice vetorial, o Spanner vai realizar a filtragem no nível da folha do índice para melhorar a performance da consulta. Recomendamos armazenar uma coluna se ela for usada em uma condição de filtragem.Use colunas de chave não embedding no índice vetorial. As colunas de chave são semelhantes às colunas
STORINGouINCLUDE, mas permitem que o mecanismo de consulta execute a filtragem com mais eficiência durante a pesquisa vetorial. Para mais informações, consulte Criar índice vetorial (GoogleSQL) ou Instruções de índice (PostgreSQL).
Ao criar a tabela, a coluna de embedding precisa ser uma matriz do tipo de dados
FLOAT32 (GoogleSQL) ou float4[] (PostgreSQL) (recomendado) e ter uma
anotação de comprimento do vetor (vector_length=>N para GoogleSQL ou VECTOR LENGTH N para PostgreSQL),
indicando a dimensão dos vetores.
O comprimento ideal do vetor depende da sua carga de trabalho, do tamanho do conjunto de dados e dos recursos computacionais disponíveis. Faça testes com dimensões diferentes para encontrar o menor tamanho que mantenha a acurácia e o desempenho do aplicativo.
A instrução DDL a seguir cria uma tabela Documents com uma coluna de embedding DocEmbedding com um comprimento de vetor:
GoogleSQL
CREATE TABLE Documents (
UserId INT64 NOT NULL,
DocId INT64 NOT NULL,
Author STRING (1024),
DocContents Bytes(MAX),
DocEmbedding ARRAY<FLOAT32>(vector_length=>128) NOT NULL,
NullableDocEmbedding ARRAY<FLOAT32>(vector_length=>128),
WordCount INT64
) PRIMARY KEY (DocId);
PostgreSQL
CREATE TABLE documents (
user_id bigint not null,
doc_id bigint not null,
author varchar(1024),
doc_contents bytea,
doc_embedding float4[] VECTOR LENGTH 128 not null,
nullable_doc_embedding float4[] VECTOR LENGTH 128,
word_count bigint,
PRIMARY KEY (doc_id)
);
Depois de preencher a tabela Documents, você pode criar um índice vetorial com uma árvore de dois níveis e 1.000 nós folha na tabela Documents com uma coluna de embedding DocEmbedding usando a distância do cosseno:
GoogleSQL
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(DocEmbedding)
STORING (WordCount)
OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);
PostgreSQL
CREATE INDEX doc_embedding_index
ON documents
USING scann(doc_embedding)
INCLUDE (word_count)
WITH (distance_type = 'COSINE', num_leaves = 1000)
WHERE doc_embedding IS NOT NULL;
Se a coluna de embedding não estiver marcada como NOT NULL na definição da tabela, você
deve declará-la com uma cláusula WHERE COLUMN_NAME IS NOT NULL na definição do índice vetorial, em que COLUMN_NAME é o nome da coluna de embedding. Para criar um índice vetorial com uma árvore de três níveis e 1.000.000 de nós folha na coluna de embedding anulável NullableDocEmbedding usando a distância do cosseno:
GoogleSQL
CREATE VECTOR INDEX DocEmbeddingThreeLevelIndex
ON Documents(NullableDocEmbedding)
STORING (WordCount)
WHERE NullableDocEmbedding IS NOT NULL
OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);
PostgreSQL
CREATE INDEX doc_embedding_index
ON documents
USING scann(nullable_doc_embedding)
INCLUDE (word_count)
WITH (distance_type = 'COSINE', tree_depth = 3, num_branches = 1000, num_leaves = 1000000)
WHERE nullable_doc_embedding IS NOT NULL;
Filtrar um índice vetorial
Também é possível criar um índice vetorial filtrado para encontrar os itens mais semelhantes no banco de dados que correspondam à condição de filtro. Um índice vetorial filtrado indexa seletivamente as linhas que atendem às condições de filtro especificadas, melhorando a performance da pesquisa.
No exemplo a seguir, a tabela Documents2 tem uma coluna chamada Category.
Na pesquisa vetorial, queremos indexar a categoria "Tech". Por isso, criamos uma coluna gerada que é avaliada como NULL se a condição de categoria não for atendida.
GoogleSQL
CREATE TABLE Documents2 (
UserId INT64 NOT NULL,
DocId INT64 NOT NULL,
DocName STRING (1024),
Author STRING (1024),
DocContents Bytes(MAX),
Category STRING(MAX),
NullIfFiltered BOOL AS (IF(Category = 'Tech', TRUE, NULL)) HIDDEN,
DocEmbedding ARRAY<FLOAT32>(vector_length=>128)
) PRIMARY KEY (DocId);
PostgreSQL
CREATE TABLE documents2 (
user_id bigint not null,
doc_id bigint not null,
doc_name varchar(1024),
author varchar(1024),
doc_contents bytea,
category varchar,
null_if_filtered boolean GENERATED ALWAYS AS (CASE WHEN category = 'Tech' THEN true END) VIRTUAL HIDDEN,
doc_embedding float4[] VECTOR LENGTH 128,
PRIMARY KEY (doc_id)
);
Em seguida, criamos um índice vetorial com um filtro. O índice vetorial TechDocEmbeddingIndex indexa apenas documentos na categoria "Tech".
GoogleSQL
CREATE VECTOR INDEX TechDocEmbeddingIndex
ON Documents2(DocEmbedding)
STORING(NullIfFiltered)
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
OPTIONS (...);
PostgreSQL
CREATE INDEX tech_doc_embedding_index
ON documents2
USING scann(doc_embedding)
INCLUDE (null_if_filtered)
WITH (distance_type = 'COSINE', num_leaves = 1000)
WHERE doc_embedding IS NOT NULL AND null_if_filtered IS NOT NULL;
Quando o Spanner executa a consulta a seguir, que tem filtros que correspondem ao TechDocEmbeddingIndex, ele é escolhido automaticamente e acelerado por TechDocEmbeddingIndex. A consulta pesquisa apenas documentos na categoria "Tech". Também é possível usar a dica FORCE_INDEX (@{FORCE_INDEX=TechDocEmbeddingIndex} para GoogleSQL ou /*@ FORCE_INDEX = tech_doc_embedding_index */ para PostgreSQL) para forçar
o Spanner a usar o índice explicitamente.
GoogleSQL
SELECT *
FROM Documents2
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
ORDER BY APPROX_(....)
LIMIT 10;
PostgreSQL
SELECT *
FROM documents2
WHERE doc_embedding IS NOT NULL AND null_if_filtered IS NOT NULL
ORDER BY spanner.approx_cosine_distance(doc_embedding, ARRAY[1.0::float4, 2.0::float4, 3.0::float4])
LIMIT 10;
Para melhorar a performance da consulta, inclua colunas de chave não embedding no índice vetorial. Isso permite que o mecanismo de consulta execute a filtragem com mais eficiência durante a pesquisa vetorial.
Na instrução de criação de índice, liste essas colunas de chave adicionais após a coluna de embedding. Por exemplo, a instrução a seguir cria um índice vetorial que inclui as colunas de chave DocName e Author para uma filtragem mais eficiente:
GoogleSQL
CREATE VECTOR INDEX DocEmbeddingIndexWithKeys
ON Documents2(DocEmbedding, DocName, Author)
STORING(NullIfFiltered)
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
OPTIONS (...);
PostgreSQL
CREATE INDEX doc_embedding_index_with_keys
ON documents2
USING scann(doc_embedding, doc_name, author)
INCLUDE (null_if_filtered)
WITH (distance_type = 'COSINE', num_leaves = 1000)
WHERE doc_embedding IS NOT NULL AND null_if_filtered IS NOT NULL;
A seguir
Saiba mais sobre os vizinhos mais próximos aproximados do Spanner .
Saiba mais sobre as funções de distância aproximada no GoogleSQL e no PostgreSQL.
Saiba mais sobre as instruções de índice para GoogleSQL
VECTOR INDEXe PostgreSQLINDEX.Saiba mais sobre as práticas recomendadas de índice vetorial.