En esta página, se explica cómo crear y administrar índices vectoriales de Spanner, que usan la búsqueda de vecino más cercano aproximado (ANN) y estructuras basadas en árboles para acelerar las búsquedas de similitud de vectores en tus datos.
Spanner acelera las búsquedas vectoriales de vecino más cercano aproximado (ANN) mediante el uso de un índice vectorial especializado. Este índice aprovecha la búsqueda escalable de vecino más cercano (ScaNN) de Google Research's , un algoritmo de vecino más cercano muy eficiente.
El índice vectorial usa una estructura basada en árboles para particionar datos y facilitar búsquedas más rápidas. Spanner ofrece configuraciones de árboles de dos y tres niveles:
- Configuración de árbol de dos niveles: Los nodos hoja (
num_leaves) contienen grupos de vectores estrechamente relacionados junto con su centroide correspondiente. El nivel raíz consta de los centroides de todos los nodos hoja. - Configuración de árbol de tres niveles: Es similar en concepto a un árbol de dos niveles, pero introduce una capa de rama adicional (
num_branches), desde la cual los centroides de los nodos hoja se particionan aún más para formar el nivel raíz (num_leaves).
Spanner elige un índice por ti. Sin embargo, si sabes que un
índice específico funciona mejor, puedes usar la FORCE_INDEX sugerencia
para elegir usar el índice vectorial más adecuado para tu caso de uso.
Para obtener más información, consulta las instrucciones
VECTOR INDEX para GoogleSQL
y las instrucciones INDEX para PostgreSQL.
Limitaciones
- No puedes dividir previamente los índices vectoriales. Para obtener más información, consulta Descripción general de la división previa.
Crear índice vectorial
Para optimizar la recuperación y el rendimiento de un índice vectorial, te recomendamos lo siguiente:
Crea tu índice vectorial después de que se escriban la mayoría de las filas con incorporaciones en tu base de datos. Es posible que también debas volver a compilar el índice vectorial de forma periódica después de insertar datos nuevos. Para obtener más información, consulta Cómo volver a compilar el índice vectorial.
En el caso de GoogleSQL, usa la cláusula
STORINGy, en el caso de PostgreSQL, usa la cláusulaINCLUDEpara almacenar una copia de una columna en el índice vectorial. Si un valor de columna se almacena en el índice vectorial, Spanner realiza el filtrado en el nivel de hoja del índice para mejorar el rendimiento de las consultas. Te recomendamos que almacenes una columna si se usa en una condición de filtrado.Usa columnas de clave que no sean de incorporación en el índice vectorial. Las columnas de clave son similares a las columnas
STORINGoINCLUDE, pero permiten que el motor de consultas realice el filtrado de manera más eficiente durante la búsqueda de vectores. Para obtener más información, consulta Crear índice vectorial (GoogleSQL) o Instrucciones de índice (PostgreSQL).
Cuando creas tu tabla, la columna de incorporación debe ser un array del
FLOAT32 (GoogleSQL) o float4[] (PostgreSQL) tipo de datos (recomendado) y tener una
anotación de longitud vectorial (vector_length=>N para GoogleSQL o VECTOR LENGTH N para PostgreSQL),
que indica la dimensión de los vectores.
La longitud vectorial óptima depende de tu carga de trabajo, el tamaño del conjunto de datos y los recursos computacionales disponibles. Experimenta con diferentes dimensiones para encontrar el tamaño más pequeño que mantenga la precisión y el rendimiento de tu aplicación.
La siguiente instrucción DDL crea una tabla Documents con una columna de incorporación DocEmbedding con una longitud vectorial:
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)
);
Después de propagar tu tabla Documents, puedes crear un índice vectorial con un árbol de dos niveles y 1,000 nodos hoja en la tabla Documents con una columna de incorporación DocEmbedding usando la distancia del coseno:
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;
Si tu columna de incorporación no está marcada como NOT NULL en la definición de la tabla, debes declararla con una cláusula WHERE COLUMN_NAME IS NOT NULL en la definición del índice vectorial, en la que COLUMN_NAME es el nombre de tu columna de incorporación. Para crear un índice vectorial con un árbol de tres niveles y 1,000,000 de nodos hoja en la columna de incorporación anulable NullableDocEmbedding usando la distancia del coseno:
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 un índice vectorial
También puedes crear un índice vectorial filtrado para encontrar los elementos más similares en tu base de datos que coincidan con la condición de filtro. Un índice vectorial filtrado indexa de forma selectiva las filas que satisfacen las condiciones de filtro especificadas, lo que mejora el rendimiento de la búsqueda.
En el siguiente ejemplo, la tabla Documents2 tiene una columna llamada Category.
En nuestra búsqueda de vectores, queremos indexar la categoría "Tech", por lo que creamos una columna generada que se evalúa como NULL si no se cumple la condición de categoría.
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)
);
Luego, creamos un índice vectorial con un filtro. El índice vectorial TechDocEmbeddingIndex solo indexa documentos en la categoría "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;
Cuando Spanner ejecuta la siguiente consulta, que tiene filtros que coinciden con TechDocEmbeddingIndex, se selecciona automáticamente y se acelera con TechDocEmbeddingIndex. La consulta solo busca documentos en la categoría "Tech". También puedes usar la sugerencia FORCE_INDEX (@{FORCE_INDEX=TechDocEmbeddingIndex} para GoogleSQL o /*@ FORCE_INDEX = tech_doc_embedding_index */ para PostgreSQL) para forzar
a Spanner a usar el índice de forma explícita.
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 mejorar el rendimiento de las consultas, puedes incluir columnas de clave que no sean de incorporación en tu índice vectorial. Esto permite que el motor de consultas realice el filtrado de manera más eficiente durante la búsqueda de vectores.
En la instrucción de creación de índice, debes enumerar estas columnas de clave adicionales después de la columna de incorporación. Por ejemplo, la siguiente instrucción crea un índice vectorial que incluye las columnas de clave DocName y Author para un filtrado más 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;
¿Qué sigue?
Obtén más información sobre los vecinos más cercanos aproximados de Spanner approximate nearest neighbors.
Obtén más información sobre las funciones de distancia aproximada en GoogleSQL y PostgreSQL.
Obtén más información sobre las instrucciones de índice para GoogleSQL
VECTOR INDEXy PostgreSQLINDEX.Obtén más información sobre las prácticas recomendadas para los índices vectoriales.