Esta página explica como criar e gerenciar índices vetoriais do Spanner, que usam a pesquisa aproximada do vizinho mais próximo (ANN) e estruturas baseadas em árvores para acelerar as pesquisas de similaridade vetorial nos seus dados.
O Spanner acelera as pesquisas de vetores de vizinho mais próximo aproximado (ANN) usando um índice de vetor especializado. Esse índice usa o vizinho mais próximo escalonável (ScaNN) da Pesquisa do Google, um algoritmo de vizinho mais próximo altamente eficiente.
O índice de vetor usa uma estrutura baseada em árvore para particionar dados e facilitar pesquisas mais rápidas. O Spanner oferece configurações de árvore 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 estreitamente relacionados e o 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 em conceito a uma árvore de dois níveis, mas introduzindo uma camada de ramificação adicional (
num_branches), de onde os centroides do nó 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 dica FORCE_INDEX
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 de vetor. 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 de vetor, recomendamos que você:
Crie o índice vetorial depois que a maioria das linhas com embeddings for gravada no banco de dados. Talvez seja necessário reconstruir periodicamente o índice de vetor 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 de vetor. Se um valor de coluna for armazenado no índice de vetor, o Spanner vai realizar a filtragem no nível da folha do índice para melhorar o desempenho da consulta. Recomendamos que você armazene uma coluna se ela for usada em uma condição de filtro.Use colunas de chave não de embedding no índice de vetor. As colunas de chave são semelhantes às colunas
STORINGouINCLUDE, mas permitem que o mecanismo de consulta faça a filtragem de maneira mais eficiente durante a pesquisa vetorial. Para mais informações, consulte Criar índice de vetor (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 tamanho ideal do vetor depende da sua carga de trabalho, do tamanho do conjunto de dados e dos recursos computacionais disponíveis. Teste diferentes dimensões para encontrar o menor tamanho que mantenha a precisão e o desempenho do seu aplicativo.
A instrução DDL a seguir cria uma tabela Documents com uma coluna de incorporação 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, crie um índice de vetor 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 de 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, declare-a com uma cláusula WHERE COLUMN_NAME IS NOT NULL na definição do índice de vetor, em que COLUMN_NAME é o nome da coluna de embedding. Para criar um índice de vetor com uma árvore de três níveis e 1.000.000 nós folha na coluna de embedding anulável NullableDocEmbedding usando a distância de 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 correspondem à condição de filtro. Um índice de vetor filtrado indexa seletivamente as linhas que atendem às condições de filtro especificadas, melhorando o desempenho da pesquisa.
No exemplo a seguir, a tabela Documents2 tem uma coluna chamada Category.
Na nossa pesquisa vetorial, queremos indexar a categoria "Tecnologia". Por isso, criamos uma coluna gerada que resulta em 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 de vetor TechDocEmbeddingIndex indexa apenas documentos na categoria "Tecnologia".
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 a TechDocEmbeddingIndex, ele escolhe e é acelerado automaticamente por TechDocEmbeddingIndex. A consulta pesquisa apenas documentos na categoria "Tecnologia". Você também pode 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 o desempenho da consulta, inclua colunas de chave não incorporadas no seu índice de vetor. Isso permite que o mecanismo de consulta realize a filtragem de maneira mais eficiente durante a pesquisa vetorial.
Na instrução de criação do índice, liste essas colunas de chave adicionais depois da coluna de incorporação. Por exemplo, a instrução a seguir cria um índice de vetores 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 instruções de índice para GoogleSQL
VECTOR INDEXe PostgreSQLINDEX.Saiba mais sobre as práticas recomendadas de índice vetorial.