Cette page explique comment créer et gérer des index vectoriels Spanner, qui utilisent la recherche approximative du voisin le plus proche (ANN) et des structures arborescentes pour accélérer les recherches de similarité vectorielle dans vos données.
Spanner accélère les recherches vectorielles approximatives du voisin le plus proche (ANN) à l'aide d'un index vectoriel spécialisé. Cet index exploite l'algorithme Scalable Nearest Neighbor (ScaNN) de Google Research, un algorithme de recherche du voisin le plus proche très efficace.
L'index vectoriel utilise une structure arborescente pour partitionner les données et faciliter les recherches plus rapides. Spanner propose des configurations d'arborescence à deux et trois niveaux :
- Configuration d'arborescence à deux niveaux : les nœuds feuilles (
num_leaves) contiennent des groupes de vecteurs étroitement liés ainsi que leur centroïde correspondant. Le niveau racine est constitué des centroïdes de tous les nœuds feuilles. - Configuration d'arborescence à trois niveaux : conceptuellement similaire à une arborescence à deux niveaux, mais avec une couche de branche supplémentaire (
num_branches) à partir de laquelle les centroïdes des nœuds feuilles sont partitionnés pour former le niveau racine (num_leaves).
Spanner choisit un index pour vous. Toutefois, si vous savez qu'un
index spécifique fonctionne mieux, vous pouvez utiliser l'FORCE_INDEXindicateur
pour choisir d'utiliser l'index vectoriel le plus approprié à votre cas d'utilisation.
Pour en savoir plus, consultez les instructions
VECTOR INDEX pour GoogleSQL
et les instructions INDEX pour PostgreSQL.
Limites
- Vous ne pouvez pas pré-fractionner les index vectoriels. Pour en savoir plus, consultez la présentation du pré-fractionnement.
Créer un index vectoriel
Pour optimiser le rappel et les performances d'un index vectoriel, nous vous recommandons de procéder comme suit :
Créez votre index vectoriel une fois que la plupart des lignes avec des embeddings sont écrites dans votre base de données. Vous devrez peut-être aussi reconstruire périodiquement l'index vectoriel après avoir inséré de nouvelles données. Pour en savoir plus, consultez la section Reconstruire l'index vectoriel.
Pour GoogleSQL, utilisez la clause
STORING, et pour PostgreSQL, utilisez la clauseINCLUDEafin de stocker une copie d'une colonne dans l'index vectoriel. Si une valeur de colonne est stockée dans l'index vectoriel, Spanner effectue un filtrage au niveau feuille de l'index pour améliorer les performances des requêtes. Nous vous recommandons de stocker une colonne si elle est utilisée dans une condition de filtrage.Utilisez des colonnes clés sans embedding dans l'index vectoriel. Les colonnes clés sont semblables aux colonnes
STORINGouINCLUDE, mais permettent au moteur de requête d'effectuer un filtrage plus efficace lors de la recherche vectorielle. Pour en savoir plus, consultez Créer un index vectoriel (GoogleSQL) ou Instructions d'index (PostgreSQL).
Lorsque vous créez votre table, la colonne d'embedding doit être un tableau de type de données
FLOAT32 (GoogleSQL) ou float4[] (PostgreSQL) (recommandé) et comporter une
annotation de longueur de vecteur (vector_length=>N pour GoogleSQL ou VECTOR LENGTH N pour PostgreSQL),
indiquant la dimension des vecteurs.
La longueur de vecteur optimale dépend de votre charge de travail, de la taille de l'ensemble de données et des ressources de calcul disponibles. Essayez différentes dimensions pour trouver la plus petite taille qui maintient la précision et les performances de votre application.
L'instruction LDD suivante crée une table Documents avec une colonne d'embedding DocEmbedding et une longueur de vecteur :
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)
);
Une fois que vous avez rempli votre table Documents, vous pouvez créer un index vectoriel avec une arborescence à deux niveaux et 1 000 nœuds feuilles sur la table Documents avec une colonne d'embedding DocEmbedding à l'aide de la distance cosinus :
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 votre colonne d'embedding n'est pas marquée comme NOT NULL dans la définition de la table, vous
devez la déclarer avec une clause WHERE COLUMN_NAME IS NOT NULL dans la définition de l'index vectoriel, où COLUMN_NAME est le nom de votre colonne d'embedding. Pour créer un index vectoriel avec une arborescence à trois niveaux et 1 000 000 de nœuds feuilles sur la colonne d'embedding pouvant accepter la valeur nulle NullableDocEmbedding à l'aide de la distance cosinus :
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;
Filtrer un index vectoriel
Vous pouvez également créer un index vectoriel filtré pour trouver les éléments les plus similaires de votre base de données qui correspondent à la condition de filtre. Un index vectoriel filtré indexe de manière sélective les lignes qui répondent aux conditions de filtre spécifiées, ce qui améliore les performances de recherche.
Dans l'exemple suivant, la table Documents2 comporte une colonne appelée Category.
Dans notre recherche vectorielle, nous voulons indexer la catégorie "Tech". Nous créons donc une colonne générée qui prend la valeur NULL si la condition de catégorie n'est pas remplie.
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)
);
Ensuite, nous créons un index vectoriel avec un filtre. L'index vectoriel TechDocEmbeddingIndex n'indexe que les documents de la catégorie "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;
Lorsque Spanner exécute la requête suivante, qui comporte des filtres correspondant à TechDocEmbeddingIndex, il sélectionne automatiquement TechDocEmbeddingIndex et l'accélère. La requête ne recherche que les documents de la catégorie "Tech". Vous pouvez également utiliser l'indicateur FORCE_INDEX (@{FORCE_INDEX=TechDocEmbeddingIndex} pour GoogleSQL ou /*@ FORCE_INDEX = tech_doc_embedding_index */ pour PostgreSQL) afin de forcer
Spanner à utiliser explicitement l'index.
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;
Pour améliorer les performances des requêtes, vous pouvez inclure des colonnes clés sans embedding dans votre index vectoriel. Cela permet au moteur de requête d'effectuer un filtrage plus efficace lors de la recherche vectorielle.
Dans l'instruction de création d'index, vous devez répertorier ces colonnes clés supplémentaires après la colonne d'embedding. Par exemple, l'instruction suivante crée un index vectoriel qui inclut les colonnes clés DocName et Author pour un filtrage plus efficace :
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;
Étape suivante
En savoir plus sur les voisins les plus proches approximatifs de Spanner .
En savoir plus sur les fonctions de distance approximative dans GoogleSQL et PostgreSQL.
En savoir plus sur les instructions d'index pour GoogleSQL
VECTOR INDEXet PostgreSQLINDEX.En savoir plus sur les bonnes pratiques concernant les index vectoriels.