Aperçu de la recherche vectorielle - Amazon MemoryDB

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Aperçu de la recherche vectorielle

La recherche vectorielle repose sur la création, la maintenance et l'utilisation d'index. Chaque opération de recherche vectorielle spécifie un seul index et son opération est limitée à cet index, c'est-à-dire que les opérations sur un index ne sont pas affectées par les opérations sur un autre index. À l'exception des opérations de création et de destruction d'index, un certain nombre d'opérations peuvent être effectuées sur n'importe quel index à tout moment, ce qui signifie qu'au niveau du cluster, plusieurs opérations sur plusieurs index peuvent être en cours simultanément.

Les index individuels sont des objets nommés qui existent dans un espace de noms unique, distinct des autres espaces de noms Redis OSS : clés, fonctions, etc. Chaque index est conceptuellement similaire à une table de base de données classique dans la mesure où il est structuré en deux dimensions : colonne et lignes. Chaque ligne du tableau correspond à une clé Redis OSS. Chaque colonne de l'index correspond à un membre ou à une partie de cette clé. Dans ce document, les termes clé, ligne et enregistrement sont identiques et utilisés de manière interchangeable. De même, les termes colonne, champ, chemin et membre sont essentiellement identiques et sont également utilisés de manière interchangeable.

Il n'existe aucune commande spéciale pour ajouter, supprimer ou modifier des données indexées. Au contraire, JSON les commandes existantes HASH ou qui modifient une clé figurant dans un index mettent également automatiquement à jour l'index.

Les index et le keyspace Redis OSS

Les index sont construits et gérés sur un sous-ensemble de l'espace de touches Redis OSS. Plusieurs index peuvent choisir des sous-ensembles disjoints ou superposés de l'espace de touches Redis OSS sans limitation. L'espace-clé de chaque index est défini par une liste de préfixes clés fournis lors de la création de l'index. La liste des préfixes est facultative et si elle est omise, l'espace de touches complet de Redis OSS fera partie de cet index. Les index sont également saisis dans la mesure où ils ne couvrent que les clés dont le type correspond. Actuellement, seuls les index JSON et HASH sont pris en charge. Un index HASH indexe uniquement les clés HASH couvertes par sa liste de préfixes. De même, un index JSON indexe uniquement les clés JSON couvertes par sa liste de préfixes. Les clés de la liste de préfixes d'espaces de touches d'un index qui n'ont pas le type désigné sont ignorées et n'affectent pas les opérations de recherche.

Lorsqu'une commande HASH ou JSON modifie une clé qui se trouve dans un espace clé d'un index, cet index est mis à jour. Ce processus consiste à extraire les champs déclarés pour chaque index et à mettre à jour l'index avec la nouvelle valeur. Le processus de mise à jour est effectué dans un fil de discussion en arrière-plan, ce qui signifie que les index ne sont cohérents qu'en fin de compte avec le contenu de leur espace clavier. Ainsi, l'insertion ou la mise à jour d'une clé ne sera pas visible dans les résultats de recherche pendant une courte période. Pendant les périodes de forte charge du système et/ou de forte mutation des données, le délai de visibilité peut s'allonger.

La création d'un index est un processus en plusieurs étapes. La première étape consiste à exécuter la commande FT.CREATE qui définit l'index. L'exécution réussie d'une création initie automatiquement la deuxième étape : le remblayage. Le processus de remplissage s'exécute dans un fil d'arrière-plan et analyse l'espace clé de Redis OSS à la recherche de clés figurant dans la liste des préfixes du nouvel index. Chaque clé trouvée est ajoutée à l'index. Finalement, l'espace clé entier est scanné, complétant ainsi le processus de création de l'index. Notez que lorsque le processus de remplissage d'index est en cours d'exécution, les mutations de clés indexées sont autorisées, il n'y a aucune restriction et le processus de remplissage d'index ne sera pas terminé tant que toutes les clés ne seront pas correctement indexées. Les opérations de requête tentées alors qu'un index est en cours de remblayage ne sont pas autorisées et se terminent par une erreur. L'achèvement du processus de remblayage peut être déterminé à partir de la sortie de la FT.INFO commande pour cet index ('backfill_status').

Types de champs d'index

Chaque champ (colonne) d'un index possède un type spécifique déclaré lors de la création de l'index et un emplacement dans une clé. Pour les clés HASH, l'emplacement est le nom du champ dans le HASH. Pour les clés JSON, l'emplacement est une description du chemin JSON. Lorsqu'une clé est modifiée, les données associées aux champs déclarés sont extraites, converties dans le type déclaré et stockées dans l'index. Si les données sont manquantes ou ne peuvent pas être converties correctement dans le type déclaré, ce champ est omis de l'index. Il existe quatre types de champs, comme expliqué ci-dessous :

  • Les champs numériques contiennent un seul chiffre. Pour les champs JSON, les règles numériques des numéros JSON doivent être respectées. Pour le HASH, le champ doit contenir le texte ASCII d'un nombre écrit au format standard pour les nombres fixes ou à virgule flottante. Quelle que soit la représentation dans la clé, ce champ est converti en un nombre à virgule flottante de 64 bits pour être stocké dans l'index. Les champs numériques peuvent être utilisés avec l'opérateur de recherche par plage. Comme les nombres sous-jacents sont stockés en virgule flottante avec ses limites de précision, les règles habituelles relatives aux comparaisons numériques pour les nombres à virgule flottante s'appliquent.

  • Les champs de balises contiennent zéro ou plusieurs valeurs de balise codées sous la forme d'une seule chaîne UTF-8. La chaîne est analysée en valeurs de balise à l'aide d'un caractère séparateur (la valeur par défaut est une virgule mais peut être remplacée), les espaces blancs de début et de fin étant supprimés. Un seul champ de balise peut contenir autant de valeurs de balise que vous le souhaitez. Les champs de balises peuvent être utilisés pour filtrer les requêtes relatives à l'équivalence des valeurs de balises par une comparaison entre majuscules et minuscules.

  • Les champs de texte contiennent un blob d'octets qui n'ont pas besoin d'être conformes à la norme UTF-8. Les champs de texte peuvent être utilisés pour décorer les résultats des requêtes avec des valeurs pertinentes pour l'application. Par exemple, une URL ou le contenu d'un document, etc.

  • Les champs vectoriels contiennent un vecteur de nombres, également appelé incorporation. Les champs vectoriels prennent en charge la recherche du plus proche voisin (KNN) de vecteurs de taille fixe à l'aide d'un algorithme et d'une métrique de distance spécifiés. Pour les index HASH, le champ doit contenir le vecteur entier codé au format binaire (little-endian IEEE 754). Pour les clés JSON, le chemin doit faire référence à un tableau de taille correcte rempli de nombres. Notez que lorsqu'un tableau JSON est utilisé comme champ vectoriel, la représentation interne du tableau dans la clé JSON est convertie dans le format requis par l'algorithme sélectionné, ce qui réduit la consommation de mémoire et la précision. Les opérations de lecture ultérieures à l'aide des commandes JSON produiront la valeur de précision réduite.

Algorithmes d'index vectoriel

Deux algorithmes d'index vectoriel sont fournis :

  • Plat — L'algorithme Flat est un traitement linéaire par force brute de chaque vecteur de l'indice, fournissant des réponses exactes dans les limites de précision des calculs de distance. En raison du traitement linéaire de l'indice, les temps d'exécution de cet algorithme peuvent être très élevés pour les grands indices.

  • HNSW (Hierarchical Navigable Small Worlds) — L'algorithme HNSW est une alternative qui fournit une approximation de la bonne réponse en échange de temps d'exécution nettement plus courts. L'algorithme est contrôlé par trois paramètresM, EF_CONSTRUCTION etEF_RUNTIME. Les deux premiers paramètres sont spécifiés au moment de la création de l'index et ne peuvent pas être modifiés. Le EF_RUNTIME paramètre possède une valeur par défaut qui est spécifiée lors de la création de l'index, mais qui peut ensuite être remplacée lors de n'importe quelle opération de requête individuelle. Ces trois paramètres interagissent pour équilibrer la consommation de mémoire et de processeur lors des opérations d'ingestion et de requête, ainsi que pour contrôler la qualité de l'approximation d'une recherche KNN exacte (connue sous le nom de ratio de rappel).

Les deux algorithmes de recherche vectorielle (Flat et HNSW) prennent en charge un INITIAL_CAP paramètre facultatif. Lorsqu'il est spécifié, ce paramètre préalloue de la mémoire aux index, ce qui permet de réduire la charge de gestion de la mémoire et d'augmenter les taux d'ingestion de vecteurs.

Les algorithmes de recherche vectorielle tels que HNSW peuvent ne pas gérer efficacement la suppression ou le remplacement de vecteurs précédemment insérés. L'utilisation de ces opérations peut entraîner une consommation excessive de mémoire d'index et/ou une dégradation de la qualité du rappel. La réindexation est une méthode permettant de rétablir une utilisation et/ou un rappel optimaux de la mémoire.

Expression de requête de recherche vectorielle

Les commandes FT.SEARCH et FT.AGGREGATE nécessitent une expression de requête. Cette expression est un paramètre de chaîne unique composé d'un ou de plusieurs opérateurs. Chaque opérateur utilise un champ de l'index pour identifier un sous-ensemble des clés de l'index. Plusieurs opérateurs peuvent être combinés à l'aide de combineurs booléens ainsi que de parenthèses pour améliorer ou restreindre davantage le jeu de clés collecté (ou le jeu de résultats).

Caractère générique

L'opérateur générique, l'astérisque (« * »), correspond à toutes les clés de l'index.

Plage numérique

La syntaxe de l'opérateur de plage numérique est la suivante :

<range-search> ::= '@' <numeric-field-name> ':' '[' <bound> <bound> ']' <bound> ::= <number> | '(' <number> <number> ::= <integer> | <fixed-point> | <floating-point> | 'Inf' | '-Inf' | '+Inf'

Le < numeric-field-name > doit être un champ de type déclaréNUMERIC. Par défaut, la borne est inclusive, mais une parenthèse ouverte initiale ['('] peut être utilisée pour rendre une borne exclusive. La recherche par plage peut être convertie en une comparaison relationnelle unique (<, <=, >, >=) en utilisant Inf +Inf ou -Inf comme l'une des limites. Quel que soit le format numérique spécifié (entier, virgule fixe, virgule flottante, infini), le nombre est converti en virgule flottante de 64 bits pour effectuer des comparaisons, réduisant ainsi la précision en conséquence.

Exemples
@numeric-field:[0 10] // 0 <= <value> <= 10 @numeric-field:[(0 10] // 0 < <value> <= 10 @numeric-field:[0 (10] // 0 <= <value> < 10 @numeric-field:[(0 (10] // 0 < <value> < 10 @numeric-field:[1.5 (Inf] // 1.5 <= value

Tag : comparer

La syntaxe de l'opérateur de comparaison de balises est la suivante :

<tag-search> ::= '@' <tag-field-name> ':' '{' <tag> [ '|' <tag> ]* '}'

Si l'une des balises de l'opérateur correspond à l'une des balises du champ de balise de l'enregistrement, l'enregistrement est inclus dans le jeu de résultats. Le champ conçu par <tag-field-name> doit être un champ de l'index déclaré avec typeTAG. Voici des exemples de comparaison de balises :

@tag-field:{ atag } @tag-field: { tag1 | tag2 }

Combinaisons booléennes

Les ensembles de résultats d'un opérateur numérique ou d'un opérateur de balise peuvent être combinés à l'aide de la logique booléenne : et/ou. Les parenthèses peuvent être utilisées pour regrouper les opérateurs et/ou modifier l'ordre d'évaluation. La syntaxe des opérateurs de logique booléenne est la suivante :

<expression> ::= <phrase> | <phrase> '|' <expression> | '(' <expression> ')' <phrase> ::= <term> | <term> <phrase> <term> ::= <range-search> | <tag-search> | '*'

Plusieurs termes combinés dans une phrase sont « et ». Les phrases multiples combinées avec le tube (« | ») sont de type « ou ».

Les index vectoriels prennent en charge deux méthodes de recherche différentes : le voisin le plus proche et le range. Une recherche dans le plus proche voisin permet de localiser un certain nombre, K, des vecteurs de l'index les plus proches du vecteur (de référence) fourni. C'est ce que l'on appelle communément KNN pour « K » voisins les plus proches. La syntaxe d'une recherche KNN est la suivante :

<vector-knn-search> ::= <expression> '=>[KNN' <k> '@' <vector-field-name> '$' <parameter-name> <modifiers> ']' <modifiers> ::= [ 'EF_RUNTIME' <integer> ] [ 'AS' <distance-field-name>]

Une recherche KNN vectorielle n'est appliquée qu'aux vecteurs qui répondent aux critères, <expression> lesquels peuvent être n'importe quelle combinaison des opérateurs définis ci-dessus : caractère générique, recherche par plage, recherche par étiquette et/ou combinaisons booléennes de ces derniers.

  • <k>est un entier spécifiant le nombre de vecteurs les plus proches voisins à renvoyer.

  • <vector-field-name>doit spécifier un champ de type déclaréVECTOR.

  • <parameter-name>le champ indique l'une des entrées de la PARAM table de la FT.AGGREGATE commande FT.SEARCH ou. Ce paramètre est la valeur du vecteur de référence pour les calculs de distance. La valeur du vecteur est codée dans la PARAM valeur au format binaire IEEE 754 little-endian (même encodé que pour un champ vectoriel HASH)

  • Pour les index vectoriels de type HNSW, la EF_RUNTIME clause facultative peut être utilisée pour remplacer la valeur par défaut du EF_RUNTIME paramètre établi lors de la création de l'index.

  • L'option <distance-field-name> fournit un nom de champ pour le jeu de résultats afin qu'il contienne la distance calculée entre le vecteur de référence et la clé localisée.

Une recherche par plage permet de localiser tous les vecteurs situés à une distance (rayon) spécifiée par rapport à un vecteur de référence. La syntaxe d'une recherche par plage est la suivante :

<vector-range-search> ::= ‘@’ <vector-field-name> ‘:’ ‘[’ ‘VECTOR_RANGE’ ( <radius> | ‘$’ <radius-parameter> ) $<reference-vector-parameter> ‘]’ [ ‘=’ ‘>’ ‘{’ <modifiers> ‘}’ ] <modifiers> ::= <modifier> | <modifiers>, <modifier> <modifer> ::= [ ‘$yield_distance_as’ ‘:’ <distance-field-name> ] [ ‘$epsilon’ ‘:’ <epsilon-value> ]

Où :

  • <vector-field-name>est le nom du champ vectoriel à rechercher.

  • <radius> or $<radius-parameter>est la limite de distance numérique pour la recherche.

  • $<reference-vector-parameter> est le nom du paramètre qui contient le vecteur de référence. La valeur du vecteur est codée dans la valeur PARAM au format binaire IEEE 754 little-endian (même encodage que pour un champ vectoriel HASH)

  • L'option <distance-field-name> fournit un nom de champ pour le jeu de résultats afin qu'il contienne la distance calculée entre le vecteur de référence et chaque clé.

  • L'option permet de <epsilon-value> contrôler les limites de l'opération de recherche, les vecteurs situés à distance <radius> * (1.0 + <epsilon-value>) sont parcourus à la recherche de résultats candidats. La valeur par défaut est 0,01.

commande INFO

La recherche vectorielle complète la commande Redis OSS INFO avec plusieurs sections supplémentaires de statistiques et de compteurs. Une demande de récupération de la section SEARCH permet de récupérer toutes les sections suivantes :

search_memory Section

Name (Nom) Description
search_used_memory_bytes Nombre d'octets de mémoire consommés dans toutes les structures de données de recherche
search_used_memory_human Version lisible par l'homme de ce qui précède

search_index_stats Section

Name (Nom) Description
numéro_de_index_de_recherche Nombre d'index créés
search_num_fulltext_indexes Nombre de champs non vectoriels dans tous les index
search_num_vector_indexes Nombre de champs vectoriels dans tous les index
search_num_hash_indexes Nombre d'index sur les clés de type HASH
search_num_json_indexes Nombre d'index sur les clés de type JSON
search_total_indexed_keys Nombre total de clés dans tous les index
search_total_indexed_vector Nombre total de vecteurs dans tous les index
search_total_indexed_hash_keys Nombre total de clés de type HASH dans tous les index
search_total_indexed_json_keys Nombre total de clés de type JSON dans tous les index
search_total_index_size Octets utilisés par tous les index
search_total_fulltext_index_size Octets utilisés par les structures d'index non vectorielles
search_total_vector_index_size Octets utilisés par les structures d'index vectoriels
search_max_index_lag_ms Délai d'ingestion lors de la dernière mise à jour du lot d'ingestion

search_ingestion Section

Name (Nom) Description
search_background_indexing_status État de l'ingestion. NO_ACTIVITYsignifie inactif. D'autres valeurs indiquent que des clés sont en cours d'ingestion.
search_ingestion_paused Sauf lors du redémarrage, cela doit toujours être « non ».

search_backfill Section

Note

Certains des champs documentés dans cette section ne sont visibles que lorsqu'un remblayage est en cours.

Name (Nom) Description
search_num_active_backfills Nombre d'activités de remblayage en cours
search_backfills_paused Sauf en cas de manque de mémoire, cela doit toujours être « non ».
search_current_backfill_progress_percentage % d'achèvement (0-100) du remblai actuel

search_query Section

Name (Nom) Description
search_num_active_queries Nombre de commandes en cours FT.SEARCH et FT.AGGREGATE commandes en cours

Sécurité de la recherche vectorielle

Les mécanismes de sécurité ACL (Access Control Lists) de Redis OSS pour l'accès aux commandes et aux données sont étendus pour contrôler la fonction de recherche. Le contrôle ACL des commandes de recherche individuelles est entièrement pris en charge. Une nouvelle catégorie ACL@search,, est fournie et de nombreuses catégories existantes (@fast,, @read@write, etc.) sont mises à jour pour inclure les nouvelles commandes. Les commandes de recherche ne modifient pas les données clés, ce qui signifie que le mécanisme ACL existant pour l'accès en écriture est préservé. Les règles d'accès pour les opérations HASH et JSON ne sont pas modifiées par la présence d'un index ; le contrôle d'accès normal au niveau des clés est toujours appliqué à ces commandes.

Les commandes de recherche avec un index ont également leur accès contrôlé via Redis OSS ACL. Les contrôles d'accès sont effectués au niveau de l'index complet, et non au niveau de chaque clé. Cela signifie que l'accès à un index n'est accordé à un utilisateur que s'il est autorisé à accéder à toutes les clés possibles dans la liste des préfixes d'espace-touches de cet index. En d'autres termes, le contenu réel d'un index ne contrôle pas l'accès. C'est plutôt le contenu théorique d'un index tel que défini par la liste de préfixes qui est utilisé pour le contrôle de sécurité. Il peut être facile de créer une situation dans laquelle un utilisateur a accès en lecture et/ou en écriture à une clé mais ne peut pas accéder à un index contenant cette clé. Notez que seul l'accès en lecture au keyspace est requis pour créer ou utiliser un index ; la présence ou l'absence d'accès en écriture n'est pas prise en compte.

Pour plus d'informations sur l'utilisation des ACL avec MemoryDB, voir Authentification des utilisateurs à l'aide de listes de contrôle d'accès (ACL).