Gérer les index vectoriels

Pour envoyer des commentaires ou demander de l'aide concernant cette fonctionnalité, envoyez un e-mail à l'adresse bq-vector-search@google.com.

Ce document explique comment créer et gérer des index vectoriels.

Un index vectoriel est une structure de données conçue pour permettre à la fonction VECTOR_SEARCH d'effectuer une recherche vectorielle plus efficace des représentations vectorielles continues. Lorsque VECTOR_SEARCH peut utiliser un index vectoriel, la fonction utilise la technique de recherche approximative du voisin le plus proche pour améliorer les performances de recherche, avec un compromis consistant à réduire le rappel, renvoyant ainsi des résultats plus approximatifs.

Rôles et autorisations

Pour créer un index vectoriel, vous devez disposer de l'autorisation IAM bigquery.tables.createIndex sur la table dans laquelle vous créez l'index. Pour supprimer un index vectoriel, vous devez disposer de l'autorisation bigquery.tables.deleteIndex. Chacun des rôles IAM prédéfinis suivants inclut les autorisations dont vous avez besoin pour travailler avec les index vectoriels :

  • Propriétaire de données BigQuery (roles/bigquery.dataOwner)
  • Éditeur de données BigQuery (roles/bigquery.dataEditor)

Créer un index vectoriel

Pour créer un index vectoriel, utilisez l'instruction LDD (langage de définition de données) CREATE VECTOR INDEX :

  1. Accédez à la page BigQuery.

    Accéder à BigQuery

  2. Dans l'éditeur de requête, exécutez l'instruction SQL suivante :

    CREATE [ OR REPLACE ] VECTOR INDEX [ IF NOT EXISTS ] INDEX_NAME
    ON DATASET_NAME.TABLE_NAME(COLUMN_NAME)
    OPTIONS(index_type = INDEX_TYPE,
      distance_type = DISTANCE_TYPE,
      ivf_options = '{"num_lists":NUM_LISTS}')
    

    Remplacez les éléments suivants :

    • INDEX_NAME : nom de l'index vectoriel que vous créez. Étant donné que l'index est toujours créé dans le même projet et le même ensemble de données que la table de base, il n'est pas nécessaire de les spécifier dans le nom.
    • DATASET_NAME : nom de l'ensemble de données contenant la table.
    • TABLE_NAME : nom de la table contenant la colonne avec des données de représentations vectorielles continues.
    • COLUMN_NAME : nom d'une colonne contenant les données de représentations vectorielles continues. La colonne doit être de type ARRAY<FLOAT64>. La colonne ne peut pas contenir de champs enfants. Tous les éléments du tableau doivent être différents de NULL et toutes les valeurs de la colonne doivent avoir les mêmes dimensions de tableau.
    • INDEX_TYPE : algorithme à utiliser pour créer l'index vectoriel. IVF est la seule valeur acceptée. Définir IVF crée l'index vectoriel en tant qu'index de fichier inversé (IVF). Un IVF utilise un algorithme k-moyennes pour regrouper les données vectorielles, puis partitionne les données vectorielles en fonction de ces clusters. Lorsque vous utilisez la fonction VECTOR_SEARCH pour rechercher les données vectorielles, elle peut utiliser ces partitions pour réduire la quantité de données à lire afin de déterminer un résultat.
    • DISTANCE_TYPE : spécifie le type de distance par défaut à utiliser lors de l'exécution d'une recherche vectorielle à l'aide de cet index. Les valeurs acceptées sont EUCLIDEAN et COSINE. EUCLIDEAN est la valeur par défaut.

      La création d'index utilise toujours la distance EUCLIDEAN pour l'entraînement, mais la distance utilisée dans la fonction VECTOR_SEARCH peut être différente.

      Si vous spécifiez une valeur pour l'argument distance_type de la fonction VECTOR_SEARCH, cette valeur est utilisée à la place de la valeur DISTANCE_TYPE.

    • NUM_LISTS : valeur INT64 inférieure ou égale à 5 000 qui détermine le nombre de listes créées par l'algorithme IVF. L'algorithme IVF divise l'intégralité de l'espace de données en un nombre de listes égal à NUM_LISTS, les points de données plus proches les uns des autres étant plus susceptibles d'être placés sur la même liste. Si la valeur de NUM_LISTS est faible, vous avez moins de listes avec davantage de points de données, tandis qu'une valeur plus élevée crée davantage de listes avec moins de points de données.

      Vous pouvez utiliser NUM_LISTS en combinaison avec l'argument fraction_lists_to_search dans la fonction VECTOR_SEARCH pour créer une recherche vectorielle efficace. Si vos données sont distribuées dans de nombreux petits groupes dans l'espace de représentation vectorielle, spécifiez une valeur NUM_LISTS élevée pour créer un index avec plus de listes et spécifiez une valeur fraction_lists_to_search plus faible pour avoir moins de listes à analyser dans la recherche vectorielle. Utilisez une valeur NUM_LISTS plus faible et une valeur fraction_lists_to_search plus élevée lorsque vos données sont distribuées dans des groupes plus petits et plus grands. L'utilisation d'une valeur num_lists élevée peut ralentir la création de l'index vectoriel.

      Si vous ne spécifiez pas NUM_LISTS, BigQuery calcule une valeur appropriée.

L'exemple suivant crée un index vectoriel sur la colonne embedding de my_table :

CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>);

CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
OPTIONS(index_type = 'IVF');

L'exemple suivant crée un index vectoriel sur la colonne embedding de my_table et spécifie le type de distance à utiliser et les options IVF :

CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>);

CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
OPTIONS(index_type = 'IVF', distance_type = 'COSINE',
ivf_options = '{"num_lists": 2500}')

Comprendre l'actualisation de l'index

Les index vectoriels sont entièrement gérés par BigQuery et actualisés automatiquement lorsque la table indexée est modifiée. Si vous supprimez la colonne indexée dans une table ou renommez la table elle-même, l'index vectoriel est automatiquement supprimé.

Si vous créez un index vectoriel sur une table de taille inférieure à 10 Mo, l'index vectoriel n'est pas renseigné. De même, si vous supprimez les données d'une table indexée et que sa taille est inférieure à 10 Mo, l'index vectoriel est temporairement désactivé. Dans ce cas, les requêtes de recherche vectorielle n'utilisent pas l'index et le code indexUnusedReasons de la section vectorSearchStatistics de la ressource Job est BASE_TABLE_TOO_SMALL. Sans l'index, VECTOR_SEARCH utilise automatiquement la force brute pour trouver les voisins les plus proches des représentations vectorielles continues.

Les requêtes qui utilisent la fonction VECTOR_SEARCH renvoient toujours des résultats corrects, même si une partie des données n'est pas encore indexée.

En savoir plus sur les index vectoriels

Vous pouvez vérifier l'existence et la disponibilité d'un index vectoriel en interrogeant INFORMATION_SCHEMA. Les vues suivantes contiennent des métadonnées sur des index vectoriels :

  • La vue INFORMATION_SCHEMA.VECTOR_INDEXES contient des informations sur les index vectoriels d'un ensemble de données.

    Une fois l'instruction CREATE VECTOR INDEX terminée, vous devez renseigner l'index pour pouvoir l'utiliser. Vous pouvez utiliser les colonnes last_refresh_time et coverage_percentage pour vérifier si un index vectoriel est prêt. Si l'index vectoriel n'est pas prêt, vous pouvez toujours utiliser la fonction VECTOR_SEARCH sur une table. En revanche, elle peut s'exécuter plus lentement sans l'index.

  • La vue INFORMATION_SCHEMA.VECTOR_INDEX_COLUMNS contient des informations sur les colonnes dotées d'un index vectoriel pour toutes les tables d'un ensemble de données.

  • La vue INFORMATION_SCHEMA.VECTOR_INDEX_OPTIONS contient des informations sur les options utilisées par les index vectoriels dans un ensemble de données.

Exemples d'index vectoriels

L'exemple suivant montre tous les index vectoriels actifs sur les tables de l'ensemble de données my_dataset, situées dans le projet my_project. Cela inclut leur nom, les instructions LDD utilisées pour les créer et leur pourcentage de couverture. Si une table de base indexée est inférieure à 10 Mo, son index n'est pas renseigné, auquel cas la valeur de coverage_percentage est égale à 0.

SELECT table_name, index_name, ddl, coverage_percentage
FROM my_project.my_dataset.INFORMATION_SCHEMA.VECTOR_INDEXES
WHERE index_status = 'ACTIVE';

Le résultat ressemble à ce qui suit :

+-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+
| table_name  | index_name  | ddl                                                                                           | coverage_percentage |
+-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+
| small_table | myindex1    | CREATE VECTOR INDEX `myindex1` ON `my_project.my_dataset.small_table`(embeddings)             | 100                 |
|             |             | OPTIONS (distance_type = 'EUCLIDEAN', index_type = 'IVF', ivf_options = '{"numLists": 3}')    |                     |
+-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+
| large_table | myindex2    | CREATE VECTOR INDEX `myindex2` ON `my_project.my_dataset.large_table`(vectors)                |  42                 |
|             |             | OPTIONS (distance_type = 'EUCLIDEAN', index_type = 'IVF', ivf_options = '{"numLists": 12}')   |                     |
+-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+

Exemples de colonnes d'index vectoriels

La requête suivante extrait des informations sur les colonnes comportant des index vectoriels :

SELECT table_name, index_name, index_column_name, index_field_path
FROM my_project.dataset.INFORMATION_SCHEMA.VECTOR_INDEX_COLUMNS;

Le résultat ressemble à ce qui suit :

+------------+------------+-------------------+------------------+
| table_name | index_name | index_column_name | index_field_path |
+------------+------------+-------------------+------------------+
| table1     | indexa     | a                 | a                |
| table2     | indexb     | b                 | b                |
| table3     | indexc     | c                 | c                |
+------------+------------+-------------------+------------------+

Exemples d'options d'index vectoriels

La requête suivante extrait des informations sur les options d'index vectoriels :

SELECT table_name, index_name, option_name, option_type, option_value
FROM my_project.dataset.INFORMATION_SCHEMA.VECTOR_INDEX_OPTIONS;

Le résultat ressemble à ce qui suit :

+------------+------------+------------------+------------------+--------------------+
| table_name | index_name | option_name      | option_type      | option_value       |
+------------+------------+------------------+------------------+--------------------+
| table1     | indexa     | distance_type    | STRING           | EUCLIDEAN          |
| table1     | indexa     | index_type       | STRING           | IVF                |
| table2     | indexb     | ivf_options      | STRING           | {"num_lists": 100} |
| table2     | indexb     | index_type       | STRING           | IVF                |
+------------+------------+------------------+------------------+--------------------+

Utilisation des index vectoriels

Des informations sur l'utilisation des index vectoriels sont disponibles dans les métadonnées du job qui a exécuté la requête de recherche vectorielle. Vous pouvez afficher les métadonnées du job à l'aide de la console Google Cloud, de l'outil de ligne de commande bq, de l'API BigQuery ou des bibliothèques clientes.

Lorsque vous utilisez la console Google Cloud, vous pouvez trouver des informations sur l'utilisation des index vectoriels dans les champs Mode d'utilisation de l'index vectoriel et Motifs de non-utilisation de l'index vectoriel.

Lorsque vous utilisez l'outil bq ou l'API BigQuery, vous pouvez trouver des informations sur l'utilisation des index vectoriels dans la section VectorSearchStatistics de la ressource Job.

Le mode d'utilisation de l'index indique si un index vectoriel a été utilisé en fournissant l'une des valeurs suivantes :

  • UNUSED : aucun index vectoriel n'a été utilisé.
  • PARTIALLY_USED : certaines fonctions VECTOR_SEARCH de la requête utilisaient des index vectoriels, et d'autres non.
  • FULLY_USED : chaque fonction VECTOR_SEARCH de la requête a utilisé un index vectoriel.

Lorsque la valeur du mode d'utilisation de l'index est UNUSED ou PARTIALLY_USED, les motifs de non-utilisation de l'index indiquent pourquoi les index vectoriels n'ont pas été utilisés dans la requête.

Par exemple, les résultats suivants renvoyés par bq show --format=prettyjson -j my_job_id indiquent que l'index n'a pas été utilisé, car l'option use_brute_force a été spécifiée dans la fonction VECTOR_SEARCH :

"vectorSearchStatistics": {
  "indexUnusedReasons": [
    {
      "baseTable": {
        "datasetId": "my_dataset",
        "projectId": "my_project",
        "tableId": "my_table"
      },
      "code": "INDEX_SUPPRESSED_BY_FUNCTION_OPTION",
      "message": "No vector index was used for the base table `my_project:my_dataset.my_table` because use_brute_force option has been specified."
    }
  ],
  "indexUsageMode": "UNUSED"
}

Options de gestion des index

Pour créer des index et laisser BigQuery les gérer, deux options s'offrent à vous :

  • Utiliser le pool d'emplacements partagés par défaut : lorsque les données que vous prévoyez d'indexer sont inférieures à votre limite par organisation, vous pouvez utiliser le pool d'emplacements partagé gratuit pour la gestion des index.
  • Utiliser votre propre réservation : pour obtenir une progression d'indexation plus prévisible et cohérente sur vos charges de travail de production plus importantes, vous pouvez utiliser vos propres réservations pour la gestion des index.

Utiliser les emplacements partagés

Si vous n'avez pas configuré votre projet pour utiliser une réservation dédiée pour l'indexation, la gestion des index est gérée dans le pool d'emplacements partagés gratuit, qui est soumis aux contraintes suivantes.

Si vous ajoutez des données à une table, ce qui entraîne le dépassement de la limite des tables indexées pour votre organisation, BigQuery suspend la gestion des index pour toutes les tables indexées. Dans ce cas, le champ index_status de la vue INFORMATION_SCHEMA.VECTOR_INDEXES affiche PENDING DISABLEMENT et l'index est mis en file d'attente pour suppression. Tant que l'index est en attente de désactivation, il est toujours utilisé dans les requêtes et le stockage des index vous est facturé. Une fois l'index supprimé, le champ index_status affiche l'index sous la forme TEMPORARILY DISABLED. Dans cet état, les requêtes n'utilisent pas l'index et le stockage d'index ne vous est pas facturé. Dans ce cas, le code IndexUnusedReason est BASE_TABLE_TOO_LARGE.

Si vous supprimez des données de la table et que la taille totale des tables indexées est inférieure à la limite par organisation, la gestion des index est réactivée pour toutes les tables indexées. Le champ index_status dans la vue INFORMATION_SCHEMA.VECTOR_INDEXES est ACTIVE, les requêtes peuvent utiliser l'index et le stockage d'index vous est facturé.

BigQuery ne garantit pas la capacité disponible du pool partagé ni le débit d'indexation que vous voyez. Pour les applications de production, vous pouvez utiliser des emplacements dédiés pour le traitement des index.

Utiliser votre propre réservation

Au lieu d'utiliser le pool d'emplacements partagés par défaut, vous pouvez éventuellement définir votre propre réservation pour indexer vos tables. L'utilisation de votre propre réservation garantit des performances prévisibles et cohérentes pour les jobs de gestion des index, telles que la création, l'actualisation et les optimisations en arrière-plan.

  • Il n'y a aucune limite de taille de table lorsqu'un job d'indexation s'exécute dans votre réservation.
  • L'utilisation de votre propre réservation vous donne une certaine flexibilité dans la gestion de vos index. Si vous devez créer un index très volumineux ou effectuer une mise à jour majeure d'une table indexée, vous pouvez ajouter temporairement d'autres emplacements à l'attribution.

Pour indexer les tables d'un projet avec une réservation désignée, créez une réservation dans la région où se trouvent vos tables. Ensuite, attribuez le projet à la réservation en définissant job_type sur BACKGROUND :

SQL

Utilisez l'instruction LDD CREATE ASSIGNMENT :

  1. Dans Google Cloud Console, accédez à la page BigQuery.

    Accéder à BigQuery

  2. Dans l'éditeur de requête, saisissez l'instruction suivante :

    CREATE ASSIGNMENT
      `ADMIN_PROJECT_ID.region-LOCATION.RESERVATION_NAME.ASSIGNMENT_ID`
    OPTIONS (
      assignee = 'projects/PROJECT_ID',
      job_type = 'BACKGROUND');
    

    Remplacez les éléments suivants :

    • ADMIN_PROJECT_ID : ID du projet d'administration propriétaire de la ressource de réservation
    • LOCATION : emplacement de la réservation.
    • RESERVATION_NAME : nom de la réservation
    • ASSIGNMENT_ID : ID de l'attribution

      L'ID doit être unique au projet et à l'emplacement. Il doit commencer et se terminer par une lettre minuscule ou un chiffre, et ne doit contenir que des lettres minuscules, des chiffres et des tirets.

    • PROJECT_ID : ID du projet contenant les tables à indexer. Ce projet est attribué à la réservation.

  3. Cliquez sur Exécuter.

Pour en savoir plus sur l'exécution des requêtes, consultez Exécuter une requête interactive.

bq

Exécutez la commande bq mk :

bq mk \
    --project_id=ADMIN_PROJECT_ID \
    --location=LOCATION \
    --reservation_assignment \
    --reservation_id=RESERVATION_NAME \
    --assignee_id=PROJECT_ID \
    --job_type=BACKGROUND \
    --assignee_type=PROJECT

Remplacez les éléments suivants :

  • ADMIN_PROJECT_ID : ID du projet d'administration propriétaire de la ressource de réservation
  • LOCATION : emplacement de la réservation.
  • RESERVATION_NAME : nom de la réservation
  • PROJECT_ID : ID du projet à attribuer à cette réservation

Afficher vos tâches d'indexation

Un job d'indexation est créée chaque fois qu'un index est créé ou mis à jour sur une table unique. Pour afficher des informations sur la tâche, interrogez les vues INFORMATION_SCHEMA.JOBS*. Vous pouvez filtrer les tâches d'indexation en définissant job_type IS NULL AND SEARCH(job_id, '`search_index`') dans la clause WHERE de votre requête. L'exemple suivant répertorie les cinq tâches d'indexation les plus récentes du projet my_project :

SELECT *
FROM
 region-us.INFORMATION_SCHEMA.JOBS
WHERE
  project_id  = 'my_project'
  AND job_type IS NULL
  AND SEARCH(job_id, '`search_index`')
ORDER BY
 creation_time DESC
LIMIT 5;

Choisir votre taille de réservation

Pour choisir le nombre approprié d'emplacements pour votre réservation, vous devez prendre en compte le moment où les tâches de gestion des index sont exécutées, ainsi que le nombre d'emplacements utilisés et votre utilisation au fil du temps. BigQuery déclenche une tâche de gestion des index dans les situations suivantes :

  • Vous créez un index sur une table.
  • Des données sont modifiées dans une table indexée.
  • Le schéma d'une table change, ce qui affecte les colonnes indexées.
  • Les données et les métadonnées d'index sont régulièrement optimisées ou mises à jour.

Le nombre d'emplacements dont vous avez besoin pour exécuter une tâche de gestion des index sur une table dépend des facteurs suivants :

  • Taille de la table
  • Taux d'ingestion de données dans la table
  • Taux d'instructions LMD appliquées à la table
  • Délai acceptable pour créer et gérer l'index
  • Complexité de l'index, généralement déterminée par les attributs des données, tels que le nombre de termes en double
Surveiller l'utilisation et la progression

Le meilleur moyen d'évaluer le nombre d'emplacements dont vous avez besoin pour exécuter efficacement vos tâches de gestion des index consiste à surveiller l'utilisation des emplacements et à ajuster la taille de la réservation en conséquence. La requête suivante permet d'obtenir l'utilisation quotidienne des emplacements pour les tâches de gestion des index. Seuls les 30 derniers jours sont inclus dans la région us-west1 :

SELECT
  TIMESTAMP_TRUNC(job.creation_time, DAY) AS usage_date,
  -- Aggregate total_slots_ms used for index-management jobs in a day and divide
  -- by the number of milliseconds in a day. This value is most accurate for
  -- days with consistent slot usage.
  SAFE_DIVIDE(SUM(job.total_slot_ms), (1000 * 60 * 60 * 24)) AS average_daily_slot_usage
FROM
  `region-us-west1`.INFORMATION_SCHEMA.JOBS job
WHERE
  project_id = 'my_project'
  AND job_type IS NULL
  AND SEARCH(job_id, '`search_index`')
GROUP BY
  usage_date
ORDER BY
  usage_date DESC
limit 30;

Lorsque les emplacements sont insuffisants pour exécuter des tâches de gestion des index, un index peut ne plus être synchronisé avec sa table. Par conséquent, les tâches d'indexation peuvent échouer. Dans ce cas, BigQuery recrée l'index à partir de zéro. Pour éviter la désynchronisation d'index, assurez-vous de disposer de suffisamment d'emplacements pour gérer les mises à jour d'index liées à l'ingestion et à l'optimisation des données. Pour en savoir plus sur l'utilisation des emplacements, consultez la page Graphiques de ressources pour les administrateurs.

Supprimer un index vectoriel

Lorsque vous n'avez plus besoin d'un index vectoriel ou que vous souhaitez modifier la colonne indexée sur une table, vous pouvez supprimer l'index de cette table à l'aide de l'instruction LDD DROP VECTOR INDEX.

Exemple :

DROP VECTOR INDEX my_index ON my_dataset.indexed_table;

Si une table indexée est supprimée, son index est automatiquement supprimé.

Étapes suivantes