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 sur vos données.
Spanner accélère les recherches vectorielles des plus proches voisins approximatifs (ANN) en utilisant un index vectoriel spécialisé. Cet index exploite la fonction Scalable Nearest Neighbor (ScaNN) de Google Research, un algorithme de recherche des plus proches voisins très efficace.
L'index vectoriel utilise une structure arborescente pour partitionner les données et faciliter des recherches plus rapides. Spanner propose des configurations d'arborescence à deux et trois niveaux :
- Configuration de l'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 se compose des centroïdes de tous les nœuds feuilles. - Configuration d'arborescence à trois niveaux : semblable à 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 encore 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'indication FORCE_INDEX pour choisir l'index vectoriel le plus adapté à 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 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 :
Créez votre index vectoriel une fois que la plupart des lignes avec des embeddings ont été écrites dans votre base de données. Vous devrez peut-être aussi reconstruire régulièrement l'index vectoriel après avoir inséré de nouvelles données. Pour en savoir plus, consultez Reconstruire l'index vectoriel.
Pour GoogleSQL, utilisez la clause
STORINGet pour PostgreSQL, utilisez la clauseINCLUDEpour stocker une copie d'une colonne dans l'index vectoriel. Si une valeur de colonne est stockée dans l'index vectoriel, Spanner effectue le filtrage au niveau de la 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 non d'embedding dans l'index vectoriel. Les colonnes clés sont semblables aux colonnes
STORINGouINCLUDE, mais elles 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. Testez 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'intégration DocEmbedding avec 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 un arbre à deux niveaux et 1 000 nœuds feuilles sur la table Documents avec une colonne d'intégration 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 un arbre à trois niveaux et 1 000 000 de nœuds feuilles sur la colonne d'embedding pouvant accepter la valeur NULL 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 souhaitons indexer la catégorie "Tech". Nous créons donc une colonne générée qui renvoie 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'indication FORCE_INDEX (@{FORCE_INDEX=TechDocEmbeddingIndex} pour GoogleSQL ou /*@ FORCE_INDEX = tech_doc_embedding_index */ pour PostgreSQL) pour forcer Spanner à utiliser l'index de manière explicite.
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 intégration dans votre index vectoriel. Cela permet au moteur de requête d'effectuer le filtrage plus efficacement lors de la recherche vectorielle.
Dans l'instruction de création d'index, vous devez lister ces colonnes clés supplémentaires après la colonne d'intégration. 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;
Étapes suivantes
En savoir plus sur les plus proches voisins 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 PostgreSQLINDEXEn savoir plus sur les bonnes pratiques concernant les index vectoriels