Créer un index ScaNN

Utilisez les embeddings stockés pour générer des index vectoriels ScaNN et interroger des embeddings avec AlloyDB pour PostgreSQL.

L'index ScaNN est un index de quantification basé sur une arborescence, conçu par Google pour la recherche approximative des plus proches voisins. Il permet de réduire le temps de création d'index et l'empreinte mémoire par rapport à HNSW. De plus, il peut offrir RPS par seconde plus élevé que HNSW en fonction de la charge de travail.

Avant de commencer

Avant de pouvoir commencer à créer des index, vous devez remplir les conditions préalables suivantes.

Créer un index ajusté automatiquement

Les index ScaNN réglés automatiquement simplifient la création d'index en permettant à AlloyDB de gérer et de régler la structure de l'index. Si vous avez besoin d'un contrôle précis sur l'optimisation des index, créez un index ScaNN optimisé manuellement.

Les index ajustés automatiquement peuvent être optimisés de deux manières :

  • (Par défaut) Rappel et latence de la recherche vectorielle au détriment du temps de création de l'index
  • Équilibrer le temps de création de l'index et les performances de recherche

Pour créer un index ScaNN réglé automatiquement, exécutez la commande suivante.

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)

Remplacez les éléments suivants :

  • INDEX_NAME : nom de l'index que vous souhaitez créer. Exemple :my_scann_index Les noms d'index sont partagés dans toute votre base de données. Vérifiez que chaque nom d'index est unique pour chaque table de votre base de données.

  • TABLE : table à laquelle ajouter l'index.

  • EMBEDDING_COLUMN : colonne qui stocke les données vector.

  • DISTANCE_FUNCTION : fonction de distance à utiliser avec cet index. Choisissez l'une des options suivantes :

    • Distance L2 : l2

    • Produit scalaire : dot_product

    • Distance de cosinus : cosine

Cette commande crée un index ScaNN optimisé pour les performances de recherche et effectue une maintenance automatique de l'index. Si vous souhaitez modifier l'un de ces paramètres, exécutez la commande suivante :

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (MODE='AUTO',
      OPTIMIZATION='OPTIMIZATION',
      auto_maintenance='AUTO_MAINTENANCE')

Remplacez les éléments suivants :

  • INDEX_NAME : nom de l'index que vous souhaitez créer. Exemple :my_scann_index Les noms d'index sont partagés dans toute votre base de données. Vérifiez que chaque nom d'index est unique pour chaque table de votre base de données.

  • TABLE : table à laquelle ajouter l'index.

  • EMBEDDING_COLUMN : colonne qui stocke les données vector.

  • DISTANCE_FUNCTION : fonction de distance à utiliser avec cet index. Choisissez l'une des options suivantes :

    • Distance L2 : l2

    • Produit scalaire : dot_product

    • Distance de cosinus : cosine

  • (Facultatif) OPTIMIZATION : définissez cette option sur l'une des valeurs suivantes :

    • (Par défaut) SEARCH_OPTIMIZED : optimisez à la fois le rappel et la latence de la recherche vectorielle, au détriment de temps de création d'index plus longs.

    • BALANCED : équilibrer le temps de création de l'index et les performances de recherche.

    Si OPTIMIZATION est défini, MODE='AUTO' doit également être inclus.

  • (Facultatif) AUTO_MAINTENANCE : contrôle si la maintenance automatique de l'index est activée ou désactivée. Pour en savoir plus sur la maintenance automatique, consultez Gérer les index vectoriels.

    • (Par défaut) ON : AlloyDB effectue une maintenance automatique sur l'index.

    • OFF : AlloyDB n'effectue pas de maintenance automatique sur l'index.

Créer un index ajusté manuellement

Si votre application a des exigences spécifiques en termes de temps de rappel et de création d'index, vous pouvez créer et ajuster manuellement l'index ScaNN.

Pour créer manuellement un index ScaNN pour une colonne contenant des embeddings vectoriels stockés, consultez les commandes suivantes.

Index arborescent à deux niveaux

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE);
  • INDEX_NAME : nom de l'index que vous souhaitez créer. Exemple : my_scann_index. Les noms d'index sont partagés dans toute votre base de données. Assurez-vous que chaque nom d'index est unique pour chaque table de votre base de données.
  • TABLE : table à laquelle ajouter l'index.
  • EMBEDDING_COLUMN : colonne qui stocke les données "vector".
  • DISTANCE_FUNCTION : fonction de distance à utiliser avec cet index. Choisissez l'une des options suivantes :
    • Distance L2 : l2
    • Produit scalaire : dot_product
    • Distance de cosinus : cosine
  • NUM_LEAVES_VALUE : nombre de partitions à appliquer à cet index. Définissez une valeur comprise entre 1 et 30 millions. Pour savoir comment choisir cette valeur, consultez Régler un index ScaNN.
  • QUANTIZER : type de quantificateur à utiliser. Notez que l'index ScaNN peut être chargé dans le moteur de données en colonnes pour accélérer davantage la recherche vectorielle. Choisissez l'une des options suivantes :
    • (Par défaut) SQ8 : offre un équilibre entre les performances des requêtes et une perte de rappel minimale. Elle est généralement inférieure à 1 ou 2 %.
    • Aperçu AH : le hachage asymétrique (AH) est jusqu'à quatre fois plus compressé que SQ8. Pour potentiellement améliorer les performances des requêtes lorsque le moteur de données en colonnes est activé et que les données d'index et de table sont insérées dans le moteur de données en colonnes, tenez compte de ce qui suit. Pour en savoir plus, consultez les bonnes pratiques pour ajuster ScaNN.
    • FLAT : offre le rappel le plus élevé (99 % ou plus), au détriment des performances de recherche.
  • (Facultatif) AUTO_MAINTENANCE : contrôle si la maintenance automatique de l'index est activée ou désactivée. Pour en savoir plus sur la maintenance automatique, consultez Gérer les index vectoriels.
    • (Par défaut) ON : AlloyDB effectue une maintenance automatique de l'index.
    • OFF : AlloyDB n'effectue pas de maintenance automatique sur l'index.

Index arborescent à trois niveaux

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE,
      max_num_levels = 2);
  • INDEX_NAME : nom de l'index que vous souhaitez créer. Exemple : my_scann_index. Les noms d'index sont partagés dans toute votre base de données. Assurez-vous que chaque nom d'index est unique pour chaque table de votre base de données.
  • TABLE : table à laquelle ajouter l'index.
  • EMBEDDING_COLUMN : colonne qui stocke les données "vector".
  • DISTANCE_FUNCTION : fonction de distance à utiliser avec cet index. Choisissez l'une des options suivantes :
    • Distance L2 : l2
    • Produit scalaire : dot_product
    • Distance de cosinus : cosine
  • NUM_LEAVES_VALUE : nombre de partitions à appliquer à cet index. Définissez une valeur comprise entre 1 et 30 millions. Pour savoir comment choisir cette valeur, consultez Régler un index ScaNN.
  • QUANTIZER : type de quantificateur à utiliser. Notez que l'index ScaNN peut être chargé dans le moteur de données en colonnes pour accélérer davantage la recherche vectorielle. Choisissez l'une des options suivantes :
    • (Par défaut) SQ8 : offre un équilibre entre les performances des requêtes et une perte de rappel minimale. Elle est généralement inférieure à 1 ou 2 %.
    • Aperçu AH : le hachage asymétrique (AH) est jusqu'à quatre fois plus compressé que SQ8. Pour potentiellement améliorer les performances des requêtes lorsque le moteur de données en colonnes est activé et que les données d'index et de table sont insérées dans le moteur de données en colonnes, tenez compte de ce qui suit. Pour en savoir plus, consultez les bonnes pratiques pour ajuster ScaNN.
    • FLAT : offre le rappel le plus élevé (99 % ou plus), au détriment des performances de recherche.
  • (Facultatif) AUTO_MAINTENANCE : contrôle si la maintenance automatique de l'index est activée ou désactivée. Pour en savoir plus sur la maintenance automatique, consultez Gérer les index vectoriels.
    • (Par défaut) ON : AlloyDB effectue une maintenance automatique de l'index.
    • OFF : AlloyDB n'effectue pas de maintenance automatique sur l'index.

Index arborescent à quatre niveaux

Les index arborescents à quatre niveaux sont disponibles en version preview.

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      max_num_levels = 3);
  • INDEX_NAME : nom de l'index que vous souhaitez créer. Exemple : my_scann_index. Les noms d'index sont partagés dans toute votre base de données. Assurez-vous que chaque nom d'index est unique pour chaque table de votre base de données.
  • TABLE : table à laquelle ajouter l'index.
  • EMBEDDING_COLUMN : colonne qui stocke les données "vector".
  • DISTANCE_FUNCTION : fonction de distance à utiliser avec cet index. Choisissez l'une des options suivantes :
    • Distance L2 : l2
    • Produit scalaire : dot_product
    • Distance de cosinus : cosine
  • NUM_LEAVES_VALUE : nombre de partitions à appliquer à cet index. Définissez une valeur comprise entre 1 et 30 millions. Pour savoir comment choisir cette valeur, consultez Régler un index ScaNN.
  • QUANTIZER : type de quantificateur à utiliser. Notez que l'index ScaNN peut être chargé dans le moteur de données en colonnes pour accélérer davantage la recherche vectorielle. Choisissez l'une des options suivantes :
    • (Par défaut) SQ8 : offre un équilibre entre les performances des requêtes et une perte de rappel minimale. Elle est généralement inférieure à 1 ou 2 %.
    • Aperçu AH : le hachage asymétrique (AH) est jusqu'à quatre fois plus compressé que SQ8. Pour potentiellement améliorer les performances des requêtes lorsque le moteur de données en colonnes est activé et que les données d'index et de table sont insérées dans le moteur de données en colonnes, tenez compte de ce qui suit. Pour en savoir plus, consultez les bonnes pratiques pour ajuster ScaNN.
    • FLAT : offre le rappel le plus élevé (99 % ou plus), au détriment des performances de recherche.

Convertir un index ajusté manuellement en index ajusté automatiquement

Pour convertir un index ajusté manuellement en index ajusté automatiquement, procédez comme suit :

  1. Réinitialisez tous les paramètres de requête définis pour votre index ajusté manuellement.

    ALTER INDEX INDEX_NAME RESET (PARAMETER_NAME);
    

    Remplacez les variables suivantes :

    • INDEX_NAME : nom de l'index que vous souhaitez convertir. Exemple :my_scann_index Les noms d'index sont partagés dans toute votre base de données. Vérifiez que chaque nom d'index est unique pour chaque table de votre base de données.

    • PARAMETER_NAME : liste de noms de paramètres de requête séparés par une virgule que vous souhaitez réinitialiser. Exemple :num_leaves, quantization

      Notez que vous devez réinitialiser tous les autres paramètres de requête avant de réinitialiser num_leaves.

  2. Réindexez votre index ajusté manuellement pour le convertir en index ajusté automatiquement.

    REINDEX INDEX CONCURRENTLY INDEX_NAME;
    

Créer un index ScaNN pour les types de données real[]

Pour créer un index pour une colonne d'embeddings qui utilise le type de données real[] au lieu de vector, convertissez le type de données de la colonne en vector :

CREATE INDEX INDEX_NAME ON TABLE
USING scann (CAST(EMBEDDING_COLUMN AS vector(DIMENSIONS)) DISTANCE_FUNCTION)

Remplacez les éléments suivants :

  • INDEX_NAME : nom de l'index que vous souhaitez créer. Exemple :my_scann_index Les noms d'index sont partagés dans toute votre base de données. Vérifiez que chaque nom d'index est unique pour chaque table de votre base de données.

  • TABLE : table à laquelle ajouter l'index.

  • DIMENSIONS : nombre de dimensions acceptées par le modèle.

  • EMBEDDING_COLUMN : colonne qui stocke les données vector.

  • DISTANCE_FUNCTION : fonction de distance à utiliser avec cet index. Choisissez l'une des options suivantes :

    • Distance L2 : l2

    • Produit scalaire : dot_product

    • Distance de cosinus : cosine

Afficher la progression de l'indexation

Pour afficher la progression de l'indexation, utilisez la vue pg_stat_progress_create_index :

SELECT * FROM pg_stat_progress_create_index;

La colonne phase indique l'état actuel de la création de votre index. Une fois la phase de création de l'index terminée, la ligne correspondante n'est plus visible.

Créer un index différé pour les tables vides ou celles qui ne contiennent pas assez de lignes

Par défaut, vous ne pouvez pas créer d'index ScaNN sur une table vide ou comportant moins de lignes que la valeur de l'option d'index num_leaves.

Pour contourner cette limitation, activez la création différée d'index afin qu'AlloyDB la reporte jusqu'à ce que le nombre de lignes de la table atteigne le seuil défini par num_leaves. Une fois le seuil atteint, AlloyDB commence à créer l'index en arrière-plan.

Cette opération différée est un processus non bloquant, qui permet à d'autres opérations de base de données, comme les lectures et les écritures, de se poursuivre sans interruption. Comme la reconstruction de l'index se produit en arrière-plan, la création différée d'index convient lorsque les tables ingèrent des lignes de données par petits lots. La recompilation de l'index est déclenchée automatiquement lorsque le nombre de lignes atteint le seuil.

Toutefois, si vous prévoyez d'insérer un grand nombre de lignes dans la table en une seule transaction, nous vous recommandons de fractionner la transaction en plusieurs transactions ou de générer un index ScaNN sans activer la création différée d'index.

Activer la création différée d'index

Pour activer la création différée d'index :

  1. Activez l'option scann.enable_index_maintenance (activée par défaut) et l'option scann.enable_preview_features. L'indicateur scann.enable_preview_features active également d'autres fonctionnalités d'aperçu.

    gcloud alloydb instances update INSTANCE_ID \
       --database-flags scann.enable_index_maintenance=on \
       --database-flags scann.enable_preview_features=on \
       --region=REGION_ID \
       --cluster=CLUSTER_ID \
       --project=PROJECT_ID
    

    Remplacez les éléments suivants :

    • INSTANCE_ID : ID de l'instance
    • REGION_ID : région où l'instance est placée, par exemple us-central1.
    • CLUSTER_ID : ID du cluster dans lequel l'instance est placée.
    • PROJECT_ID : ID du projet dans lequel le cluster est placé.
  2. Créez un index ScaNN. Si vous créez un index en mode manuel, assurez-vous que le paramètre auto_maintenance est défini sur on. Pour en savoir plus, consultez Créer un index ajusté manuellement.

Limites

  • Le processus en arrière-plan de création automatique d'index utilise des valeurs d'indicateurs au niveau de la base de données. Même si vous définissez des options au niveau de la session à l'aide de la commande SET LOCAL, le processus tient compte de la valeur d'option définie au niveau de la base de données.
  • Si vous prévoyez d'insérer de manière groupée une grande quantité de données dans une table vide en une seule transaction, nous vous recommandons d'exécuter la transaction d'insertion unique, puis de créer un index ScaNN.

Forcer la création d'index sur des tables vides ou de petite taille

AlloyDB utilise des validations pour empêcher la création d'un index ScaNN sur une table vide ou une table comportant très peu de lignes pour les raisons suivantes :

  • L'index ScaNN s'entraîne sur des données insuffisantes. Cela peut entraîner un mauvais rappel pour les recherches de similarité vectorielle.

  • Les performances d'écriture dans la base de données peuvent se dégrader.

Nous vous recommandons de différer la création d'index en cas de performances sous-optimales.

Toutefois, dans certains scénarios de développement ou de test, vous devrez peut-être créer un index sur une table vide ou de petite taille. Dans ce cas, vous pouvez forcer la création d'index. Notez que la création forcée d'index nécessite des droits SUPERUSER.

Pour forcer la création d'un index, procédez comme suit :

  1. Définissez le paramètre scann.allow_blocked_operations creation au niveau de la session sur true dans la base de données :

    SET scann.allow_blocked_operations = true;
    
  2. Si l'utilisateur que vous utilisez pour exécuter ces requêtes ne dispose pas des droits d'accès SUPERUSER, attribuez-les :

    CREATE USER USERNAME WITH SUPERUSER PASSWORD PASSWORD;
    

    Remplacez les variables suivantes :

    • USERNAME : nom de l'utilisateur auquel vous souhaitez accorder des droits SUPERUSER.
    • PASSWORD : mot de passe de l'utilisateur.

Créer des index en parallèle

Pour créer votre index plus rapidement, AlloyDB peut générer automatiquement plusieurs nœuds de calcul parallèles en fonction de votre ensemble de données et du type d'index que vous choisissez. Cela se produit souvent lorsque vous créez un index ScaNN à trois ou quatre niveaux, ou si votre ensemble de données dépasse 100 millions de lignes.

Bien qu'AlloyDB optimise automatiquement le nombre de nœuds de calcul parallèles, vous pouvez les ajuster à l'aide des paramètres de planification des requêtes PostgreSQL suivants :

Pour éviter les problèmes de mémoire insuffisante lorsque vous créez l'index ScaNN, assurez-vous que les indicateurs de base de données maintenance_work_mem et shared_buffers sont définis sur une valeur inférieure à la mémoire totale de la machine.

Exécuter une requête

Une fois que vous avez stocké et indexé les embeddings dans votre base de données, vous pouvez commencer à interroger vos données. Vous ne pouvez pas exécuter de requêtes de recherche groupées à l'aide de l'extension alloydb_scann.

Pour trouver les voisins sémantiques les plus proches d'une chaîne de texte, vous pouvez utiliser la fonction google_ml.embedding() pour traduire le texte en vecteur.

Comme google_ml.embedding() renvoie un tableau réel, vous devez convertir explicitement l'appel de fonction en vector avant de l'appliquer à l'un des opérateurs de voisins les plus proches, par exemple <-> pour la distance L2. Ces opérateurs peuvent ensuite utiliser l'index ScaNN pour rechercher les lignes de la base de données présentant les embeddings les plus similaires sur le plan sémantique.

SELECT * FROM TABLE
ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY
  google_ml.embedding(
      model_id => 'MODEL_ID',
      content => 'CONTENT')::vector
LIMIT ROW_COUNT

Remplacez les variables suivantes :

  • TABLE : table contenant l'embedding auquel comparer le texte.

  • EMBEDDING_COLUMN : colonne contenant les embeddings stockées.

  • DISTANCE_FUNCTION_QUERY : fonction de distance à utiliser avec cette requête. Choisissez l'équivalent de la fonction de distance pour la requête lorsque vous avez créé l'index :

    • Distance L2 : <->

    • Produit interne : <#>

    • Distance de cosinus : <=>

  • MODEL_ID : ID du modèle d'embedding enregistré que vous souhaitez utiliser.

  • CONTENT : chaîne de texte que vous souhaitez traduire en embedding et rechercher.

  • ROW_COUNT : nombre de lignes à renvoyer. Par exemple, spécifiez 1 si vous souhaitez n'obtenir que la meilleure correspondance.

Étapes suivantes