Ajuster les performances des requêtes vectorielles

Ce document explique comment optimiser vos index pour améliorer les performances des requêtes et la récupération.

Régler un indice ScaNN

L'index ScaNN utilise une indexation basée sur la quantification arborescente. Dans les techniques de quantification arborescente, les index apprennent un arbre de recherche avec une fonction de quantification (ou hachage). Lorsque vous exécutez une requête, l'arborescence de recherche est utilisée pour élaguer l'espace de recherche, tandis que la quantification permet de compresser la taille de l'index. Cette élagage accélère l'évaluation de la similarité (c'est-à-dire de la distance) entre le vecteur de requête et les vecteurs de la base de données.

Pour obtenir à la fois un nombre élevé de requêtes par seconde (RPS) et un rappel élevé avec vos requêtes de voisin le plus proche, vous devez partitionner l'arborescence de votre index ScaNN de la manière la plus adaptée à vos données et à vos requêtes.

Avant de créer un index ScaNN, procédez comme suit:

  • Assurez-vous qu'une table contenant vos données a déjà été créée.
  • Assurez-vous que la valeur que vous définissez pour l'indicateur maintenance_work_mem et shared_buffers est inférieure à la mémoire totale de la machine pour éviter les problèmes lors de la génération de l'index.

Paramètres de réglage

Les paramètres d'index et les indicateurs de base de données suivants sont utilisés ensemble pour trouver le bon équilibre entre le rappel et le RPS. Tous les paramètres s'appliquent aux deux types d'index ScaNN.

Paramètre de réglage Description Type de paramètre
num_leaves Nombre de partitions à appliquer à cet indice. Le nombre de partitions auxquelles vous appliquez un index lors de sa création affecte ses performances. En augmentant le nombre de partitions pour un nombre défini de vecteurs, vous créez un index plus précis, ce qui améliore le rappel et les performances des requêtes. Toutefois, cela entraîne des temps de création d'index plus longs.

Étant donné que les arbres à trois niveaux sont créés plus rapidement que les arbres à deux niveaux, vous pouvez augmenter la valeur num_leaves_value lorsque vous créez un indice d'arbre à trois niveaux pour obtenir de meilleures performances.
  • Indice à deux niveaux: définissez cette valeur sur une valeur comprise entre 1 et 1048576.

    Si vous ne savez pas quelle valeur exacte sélectionner, utilisez sqrt(ROWS) comme point de départ, où ROWS correspond au nombre de lignes de vecteurs. Le nombre de vecteurs stockés par chaque partition est calculé par
    ROWS/sqrt(ROWS) = sqrt(ROWS).

    Étant donné qu'un indice en arbre à deux niveaux peut être créé sur un ensemble de données comportant moins de 10 millions de lignes de vecteurs, chaque partition contiendra moins de (sqrt(10M)) vecteurs, soit 3200 vecteurs. Pour des performances optimales, nous vous recommandons de réduire le nombre de vecteurs dans chaque partition.
  • Index à trois niveaux: définissez cette valeur sur une valeur comprise entre 1 et 1048576.

    Si vous ne savez pas quelle valeur exacte sélectionner, utilisez power(ROWS, 2/3) comme point de départ, où ROWS correspond au nombre de lignes de vecteurs. Le nombre de vecteurs stockés par chaque partition est calculé par
    ROWS/power(ROWS, 2/3) = power(ROWS, 1/3).

    Étant donné qu'un indice en arbre à trois niveaux peut être créé sur un ensemble de données dont les lignes vectorielles dépassent 100 millions, chaque partition contiendra plus de
    (power(100M, 1/3)) vecteurs, soit 465 vecteurs. Pour des performances optimales, nous vous recommandons de réduire le nombre de vecteurs dans chaque partition.
Création d'index
quantizer Type de quantizeur que vous souhaitez utiliser pour l'arborescence k-means. La valeur par défaut est SQ8 pour améliorer les performances des requêtes.

Définissez-la sur FLAT pour améliorer le rappel.
Création d'index
enable_pca Active l'analyse des composants principaux (ACP), une technique de réduction des dimensions utilisée pour réduire automatiquement la taille de l'encapsulation lorsque cela est possible. Cette option est activée par défaut.

Définir sur false si vous constatez une dégradation de la mémorisation.
Création d'index
scann.num_leaves_to_search L'indicateur de base de données contrôle le compromis entre le rappel et le RPS. La valeur par défaut est de 1% de la valeur définie dans num_leaves.

Plus la valeur définie est élevée, plus le rappel est élevé, mais le RPS est plus faible, et inversement.
Exécution de la requête
scann.max_top_neighbors_buffer_size L'indicateur de base de données spécifie la taille du cache utilisé pour améliorer les performances des requêtes filtrées en attribuant une note ou un classement aux voisins candidats analysés en mémoire plutôt que sur le disque. La valeur par défaut est 20000.

Plus la valeur définie est élevée, plus le RPS est élevé pour les requêtes filtrées, mais cela entraîne une utilisation plus importante de la mémoire. À l'inverse, plus la valeur est faible, moins le débit de requêtes par seconde est élevé.
Exécution de la requête
scann.pre_reordering_num_neighbors Lorsque l'indicateur de base de données est défini, il spécifie le nombre de voisins candidats à prendre en compte lors des étapes de réorganisation après que la recherche initiale a identifié un ensemble de candidats. Définissez cette valeur sur une valeur supérieure au nombre de voisins que vous souhaitez que la requête renvoie.

Des ensembles de valeurs plus élevées améliorent le rappel, mais cette approche réduit le RPS.
Exécution de la requête
max_num_levels Nombre maximal de niveaux de l'arbre de partitionnement en k-moyennes.
  • Index d'arborescence à deux niveaux: défini par défaut pour la quantification basée sur une arborescence à deux niveaux.
  • Index d'arborescence à trois niveaux: définissez explicitement sur 2 pour la quantification basée sur une arborescence à trois niveaux.
Création d'index

Régler un indice ScaNN

Prenons les exemples suivants d'index ScaNN à deux et trois niveaux qui montrent comment les paramètres de réglage sont définis:

Index à deux niveaux

SET LOCAL scann.num_leaves_to_search = 1;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 1/2)]);

Index à trois niveaux

SET LOCAL scann.num_leaves_to_search = 10;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 2/3)], max_num_levels = 2);

Toute opération d'insertion ou de mise à jour sur une table pour laquelle un indice ScaNN est déjà généré a un impact sur la façon dont l'arbre appris optimise l'index. Si votre table est sujette à des mises à jour ou des insertions fréquentes, nous vous recommandons de réindexer régulièrement l'index ScaNN existant pour améliorer la précision de la récupération.

Vous pouvez surveiller les métriques de l'index pour déterminer le nombre de mutations créées depuis la création de l'index, puis réindexer en conséquence. Pour en savoir plus sur les métriques, consultez la section Métriques de l'index vectoriel.

Bonnes pratiques de réglage

Les recommandations de réglage de l'index varient selon le type d'index ScaNN que vous prévoyez d'utiliser. Cette section fournit des recommandations sur l'ajustement des paramètres d'index pour obtenir un équilibre optimal entre le rappel et le RPS.

Index arborescent à deux niveaux

Pour appliquer des recommandations afin de vous aider à trouver les valeurs optimales de num_leaves et num_leaves_to_search pour votre ensemble de données, procédez comme suit:

  1. Créez l'index ScaNN avec num_leaves défini sur la racine carrée du nombre de lignes de la table indexée.
  2. Exécutez vos requêtes de test, en augmentant la valeur de scann.num_of_leaves_to_search, jusqu'à atteindre la plage de rappel cible (par exemple, 95 %). Pour en savoir plus sur l'analyse de vos requêtes, consultez Analyser vos requêtes.
  3. Notez le ratio entre scann.num_leaves_to_search et num_leaves qui sera utilisé dans les étapes suivantes. Ce ratio fournit une approximation autour de l'ensemble de données qui vous aidera à atteindre votre rappel cible.

    Si vous travaillez avec des vecteurs à dimension élevée (500 dimensions ou plus) et que vous souhaitez améliorer le rappel, essayez d'ajuster la valeur de scann.pre_reordering_num_neighbors. Pour commencer, définissez la valeur sur 100 * sqrt(K), où K correspond à la limite que vous avez définie dans votre requête.
  4. Si votre RPS est trop faible une fois que vos requêtes ont atteint un rappel cible, procédez comme suit :
    1. Recréez l'index en augmentant la valeur de num_leaves et scann.num_leaves_to_search conformément aux instructions suivantes :
      • Définissez num_leaves sur un facteur plus élevé de la racine carrée du nombre de lignes. Par exemple, si num_leaves est défini sur la racine carrée du nombre de lignes de l'index, essayez de le définir sur le double de la racine carrée. Si la valeur est déjà doublée, essayez de la définir sur le triple de la racine carrée.
      • Augmentez scann.num_leaves_to_search si nécessaire pour maintenir son ratio avec num_leaves, que vous avez noté à l'étape 3.
      • Définissez num_leaves sur une valeur inférieure ou égale au nombre de lignes divisé par 100.
    2. Exécutez à nouveau les requêtes de test. Lorsque vous exécutez les requêtes de test, essayez de réduire scann.num_leaves_to_search afin de trouver une valeur qui augmente le RPS tout en maintenant un taux de rappel élevé. Essayez différentes valeurs de scann.num_leaves_to_search sans reconstruire l'index.
  5. Répétez l'étape 4 jusqu'à ce que la plage de RPS et le débit de requêtes par seconde atteignent des valeurs acceptables.

Index arborescent à trois niveaux

En plus des recommandations concernant l'index ScaNN en arbre à deux niveaux, suivez les conseils ci-dessous et la procédure d'ajustement de l'index:

  • Augmenter la valeur de max_num_levels de 1 pour un arbre à deux niveaux à 2 pour un arbre à trois niveaux réduit considérablement le temps de création d'un indice, mais au détriment de la précision du rappel. Définissez max_num_levels à l'aide de la recommandation suivante :
    • Définissez la valeur sur 2 si le nombre de lignes vectorielles dépasse 100 millions.
    • Définissez la valeur sur 1 si le nombre de lignes vectorielles est inférieur à 10 millions.
    • Définissez la valeur sur 1 ou 2 si le nombre de lignes de vecteurs est compris entre 10 et 100 millions, en fonction du temps de création de l'index et de la précision de rappel dont vous avez besoin.

Pour appliquer des recommandations afin de trouver la valeur optimale des paramètres d'index num_leaves et max_num_levels, procédez comme suit:

  1. Créez l'index ScaNN avec les combinaisons num_leaves et max_num_levels suivantes en fonction de votre ensemble de données:

    • Lignes de vecteurs supérieures à 100 millions: définissez max_num_levels sur 2 et num_leaves sur power(rows, ⅔).
    • Lignes vectorielles inférieures à 100 millions: définissez max_num_levels sur 1 et num_leaves sur sqrt(rows).
    • Lignes de vecteurs comprises entre 10 et 100 millions de lignes: commencez par définir max_num_levels sur 1 et num_leaves sur sqrt(rows).
  2. Exécutez vos requêtes de test. Pour en savoir plus sur l'analyse des requêtes, consultez Analyser vos requêtes.

    Si le temps de création de l'index est satisfaisant, conservez la valeur max_num_levels et testez la valeur num_leaves pour une précision de rappel optimale.

  3. Si vous n'êtes pas satisfait du délai de création de l'index, procédez comme suit:

    • Si la valeur max_num_levels est 1, supprimez l'index. Recréez l'index en définissant la valeur max_num_levels sur 2.

      Exécutez les requêtes et ajustez la valeur num_leaves pour obtenir une précision de rappel optimale.

    • Si la valeur max_num_levels est 2, supprimez l'index. Recréez l'index avec la même valeur max_num_levels et ajustez la valeur num_leaves pour obtenir une précision de rappel optimale.

Régler un index IVF

Ajuster les valeurs que vous définissez pour les paramètres lists, ivf.probes et quantizer peut vous aider à optimiser les performances de votre application:

Paramètre de réglage Description Type de paramètre
lists Nombre de listes créées lors de la création de l'index. La valeur de départ pour définir cette valeur est (rows)/1000 pour un maximum d'un million de lignes et sqrt(rows) pour plus d'un million de lignes. Création d'index
quantizer Type de quantizeur que vous souhaitez utiliser pour l'arborescence k-means. La valeur par défaut est SQ8 pour améliorer les performances des requêtes. Définissez-le sur FLAT pour améliorer le souvenir. Création d'index
ivf.probes le nombre de listes les plus proches à explorer lors de la recherche. Le point de départ de cette valeur est
sqrt(lists).
Exécution de la requête

Prenons l'exemple suivant, qui montre un indice IVF avec les paramètres de réglage définis:

SET LOCAL ivf.probes = 10;

CREATE INDEX my-ivf-index ON my-table
  USING ivf (vector_column cosine)
  WITH (lists = 100, quantizer = 'SQ8');

Régler un index IVFFlat

Ajuster les valeurs que vous définissez pour les paramètres lists et ivfflat.probes peut vous aider à optimiser les performances de l'application:

Paramètre de réglage Description Type de paramètre
lists Nombre de listes créées lors de la création de l'index. La valeur de départ pour définir cette valeur est (rows)/1000 pour un maximum d'un million de lignes et sqrt(rows) pour plus d'un million de lignes. Création d'index
ivfflat.probes Nombre de listes les plus proches à explorer lors de la recherche. Le point de départ de cette valeur est
sqrt(lists).
Exécution de la requête

Avant de créer un index IVFFlat, assurez-vous que l'indicateur max_parallel_maintenance_workers de votre base de données est défini sur une valeur suffisante pour accélérer la création d'index sur de grandes tables.

Prenons l'exemple suivant, qui montre un indice IVFFlat avec les paramètres de réglage définis:

SET LOCAL ivfflat.probes = 10;

CREATE INDEX my-ivfflat-index ON my-table
  USING ivfflat (vector_column cosine)
  WITH (lists = 100);

Régler un index HNSW

Ajuster les valeurs que vous définissez pour les paramètres m, ef_construction et hnsw.ef_search peut vous aider à optimiser les performances de l'application.

Paramètre de réglage Description Type de paramètre
m Nombre maximal de connexions par nœud du graphique. Vous pouvez commencer avec la valeur par défaut 16(par défaut) et tester des valeurs plus élevées en fonction de la taille de votre ensemble de données. Création d'index
ef_construction Taille de la liste des candidats dynamiques maintenue lors de la construction du graphique, qui met constamment à jour les meilleurs candidats actuels pour les voisins les plus proches d'un nœud. Définissez cette valeur sur une valeur supérieure à deux fois la valeur m(par exemple, 64 par défaut). Création d'index
ef_search Taille de la liste de candidats dynamiques utilisée lors de la recherche. Vous pouvez commencer par définir cette valeur sur m ou ef_construction, puis la modifier en observant le rappel. La valeur par défaut est 40. Exécution de la requête

Prenons l'exemple suivant, qui montre un indice hnsw avec les paramètres de réglage définis:

SET LOCAL hnsw.ef_search = 40;

CREATE INDEX my-hnsw-index ON my-table
  USING hnsw (vector_column cosine)
  WITH (m = 16, ef_construction = 200);

Analyser vos requêtes

Utilisez la commande EXPLAIN ANALYZE pour analyser vos insights sur les requêtes, comme illustré dans l'exemple de requête SQL suivant.

  EXPLAIN ANALYZE SELECT result-column FROM my-table
    ORDER BY EMBEDDING_COLUMN ::vector
    USING INDEX my-scann-index
    <-> embedding('textembedding-gecko@003', 'What is a database?')
    LIMIT 1;

L'exemple de réponse QUERY PLAN inclut des informations telles que le temps passé, le nombre de lignes analysées ou renvoyées, ainsi que les ressources utilisées.

Limit  (cost=0.42..15.27 rows=1 width=32) (actual time=0.106..0.132 rows=1 loops=1)
  ->  Index Scan using my-scann-index on my-table  (cost=0.42..858027.93 rows=100000 width=32) (actual time=0.105..0.129 rows=1 loops=1)
        Order By: (embedding_column <-> embedding('textgecko@003', 'What is a database?')::vector(768))
        Limit value: 1
Planning Time: 0.354 ms
Execution Time: 0.141 ms

Afficher les métriques de l'index vectoriel

Vous pouvez utiliser les métriques de l'index vectoriel pour examiner les performances de votre index vectoriel, identifier les points à améliorer et ajuster votre index en fonction des métriques, si nécessaire.

Pour afficher toutes les métriques de l'index vectoriel, exécutez la requête SQL suivante, qui utilise la vue pg_stat_ann_indexes:

SELECT * FROM pg_stat_ann_indexes;

Pour obtenir la liste complète des métriques, consultez la section Métriques de l'index vectoriel.

Étape suivante