Panoramica della ricerca vettoriale - Amazon MemoryDB

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Panoramica della ricerca vettoriale

La ricerca vettoriale si basa sulla creazione, la manutenzione e l'uso di indici. Ogni operazione di ricerca vettoriale specifica un singolo indice e il suo funzionamento è limitato a quell'indice, vale a dire che le operazioni su un indice non sono influenzate dalle operazioni su nessun altro indice. Ad eccezione delle operazioni di creazione e distruzione degli indici, è possibile eseguire un numero qualsiasi di operazioni su qualsiasi indice in qualsiasi momento, il che significa che a livello di cluster è possibile eseguire più operazioni su più indici contemporaneamente.

I singoli indici sono oggetti denominati che esistono in uno spazio dei nomi univoco, separato dagli altri spazi dei nomi Redis OSS: chiavi, funzioni, ecc. Ogni indice è concettualmente simile a una tabella di database convenzionale in quanto è strutturato in due dimensioni: colonna e righe. Ogni riga della tabella corrisponde a una chiave Redis OSS. Ogni colonna dell'indice corrisponde a un membro o parte di quella chiave. All'interno di questo documento i termini chiave, riga e record sono identici e utilizzati in modo intercambiabile. Allo stesso modo, i termini colonna, campo, percorso e membro sono essenzialmente identici e vengono utilizzati anche in modo intercambiabile.

Non esistono comandi speciali per aggiungere, eliminare o modificare i dati indicizzati. Piuttosto, anche JSON i comandi esistenti HASH o che modificano una chiave presente in un indice aggiornano automaticamente l'indice.

Indici e spazio chiave Redis OSS

Gli indici vengono creati e gestiti su un sottoinsieme dello spazio chiave Redis OSS. Più indici possono scegliere sottoinsiemi disgiunti o sovrapposti dello spazio chiave Redis OSS senza limitazioni. Lo spazio chiave per ogni indice è definito da un elenco di prefissi chiave forniti al momento della creazione dell'indice. L'elenco dei prefissi è facoltativo e, se omesso, l'intero keyspace Redis OSS farà parte di tale indice. Gli indici vengono inoltre digitati in quanto coprono solo le chiavi che hanno un tipo corrispondente. Attualmente sono supportati solo gli indici JSON e HASH. Un indice HASH indicizza solo le chiavi HASH incluse nel relativo elenco di prefissi e analogamente un indice JSON indicizza solo le chiavi JSON incluse nel relativo elenco di prefissi. Le chiavi all'interno dell'elenco dei prefissi dello spazio dei tasti di un indice che non hanno il tipo designato vengono ignorate e non influiscono sulle operazioni di ricerca.

Quando un comando HASH o JSON modifica una chiave che si trova all'interno di uno spazio chiave di un indice, tale indice viene aggiornato. Questo processo prevede l'estrazione dei campi dichiarati per ogni indice e l'aggiornamento dell'indice con il nuovo valore. Il processo di aggiornamento viene eseguito in un thread in background, il che significa che gli indici sono coerenti solo alla fine con il contenuto del loro keyspace. Pertanto l'inserimento o l'aggiornamento di una chiave non sarà visibile nei risultati di ricerca per un breve periodo di tempo. Durante i periodi di intenso carico del sistema e/o di forte mutazione dei dati, il ritardo di visibilità può aumentare.

La creazione di un indice è un processo in più fasi. Il primo passo consiste nell'eseguire il comando FT.CREATE che definisce l'indice. L'esecuzione corretta di una creazione avvia automaticamente il secondo passaggio: il backfilling. Il processo di riempimento viene eseguito in un thread in background e analizza lo spazio delle chiavi Redis OSS alla ricerca di chiavi che si trovano all'interno dell'elenco di prefissi del nuovo indice. Ogni chiave trovata viene aggiunta all'indice. Alla fine viene scansionato l'intero keyspace, completando il processo di creazione dell'indice. Nota che mentre il processo di riempimento dell'indice è in esecuzione, le mutazioni delle chiavi indicizzate sono consentite, non ci sono restrizioni e il processo di riempimento dell'indice non verrà completato finché tutte le chiavi non saranno indicizzate correttamente. Le operazioni di interrogazione tentate mentre un indice è in fase di riempimento non sono consentite e vengono terminate con un errore. Il completamento del processo di riempimento può essere determinato dall'output del FT.INFO comando per quell'indice ('backfill_status').

Tipi di campi dell'indice

Ogni campo (colonna) di un indice ha un tipo specifico che viene dichiarato al momento della creazione dell'indice e una posizione all'interno di una chiave. Per le chiavi HASH, la posizione è il nome del campo all'interno dell'HASH. Per le chiavi JSON, la posizione è una descrizione del percorso JSON. Quando una chiave viene modificata, i dati associati ai campi dichiarati vengono estratti, convertiti nel tipo dichiarato e memorizzati nell'indice. Se i dati mancano o non possono essere convertiti correttamente nel tipo dichiarato, quel campo viene omesso dall'indice. Esistono quattro tipi di campi, come spiegato di seguito:

  • I campi numerici contengono un solo numero. Per i campi JSON, è necessario seguire le regole numeriche dei numeri JSON. Per HASH, il campo dovrebbe contenere il testo ASCII di un numero scritto nel formato standard per numeri a virgola fissa o mobile. Indipendentemente dalla rappresentazione all'interno della chiave, questo campo viene convertito in un numero a virgola mobile a 64 bit per la memorizzazione all'interno dell'indice. I campi numerici possono essere utilizzati con l'operatore di ricerca per intervalli. Poiché i numeri sottostanti sono memorizzati in virgola mobile con i relativi limiti di precisione, si applicano le normali regole sui confronti numerici per i numeri in virgola mobile.

  • I campi tag contengono zero o più valori di tag codificati come una singola stringa UTF-8. La stringa viene analizzata in valori di tag utilizzando un carattere separatore (l'impostazione predefinita è una virgola ma può essere sovrascritta) con gli spazi bianchi iniziali e finali rimossi. Qualsiasi numero di valori di tag può essere contenuto in un singolo campo di tag. I campi tag possono essere utilizzati per filtrare le query per l'equivalenza dei valori dei tag con un confronto con o senza distinzione tra maiuscole e minuscole.

  • I campi di testo contengono una serie di byte che non devono necessariamente essere conformi a UTF-8. I campi di testo possono essere utilizzati per decorare i risultati delle query con valori significativi per l'applicazione. Ad esempio un URL o il contenuto di un documento, ecc.

  • I campi vettoriali contengono un vettore di numeri noto anche come incorporamento. I campi vettoriali supportano la ricerca K-Nearest Neighbor Searching (KNN) di vettori di dimensioni fisse utilizzando un algoritmo e una metrica di distanza specificati. Per gli indici HASH, il campo deve contenere l'intero vettore codificato in formato binario (Little-endian IEEE 754). Per le chiavi JSON, il percorso deve fare riferimento a un array della dimensione corretta pieno di numeri. Nota che quando un array JSON viene utilizzato come campo vettoriale, la rappresentazione interna dell'array all'interno della chiave JSON viene convertita nel formato richiesto dall'algoritmo selezionato, riducendo il consumo di memoria e la precisione. Le successive operazioni di lettura che utilizzano i comandi JSON produrranno un valore di precisione ridotto.

Algoritmi di indice vettoriale

Sono disponibili due algoritmi di indice vettoriale:

  • Flat — L'algoritmo Flat è un'elaborazione lineare a forza bruta di ogni vettore dell'indice, che fornisce risposte esatte entro i limiti della precisione dei calcoli della distanza. Grazie all'elaborazione lineare dell'indice, i tempi di esecuzione di questo algoritmo possono essere molto elevati per indici di grandi dimensioni.

  • HNSW (Hierarchical Navigable Small Worlds) — L'algoritmo HNSW è un'alternativa che fornisce un'approssimazione della risposta corretta in cambio di tempi di esecuzione notevolmente inferiori. L'algoritmo è controllato da tre parametri e. M EF_CONSTRUCTION EF_RUNTIME I primi due parametri vengono specificati al momento della creazione dell'indice e non possono essere modificati. Il EF_RUNTIME parametro ha un valore predefinito che viene specificato al momento della creazione dell'indice, ma può essere sovrascritto in ogni singola operazione di interrogazione in seguito. Questi tre parametri interagiscono per bilanciare il consumo di memoria e CPU durante le operazioni di inserimento e interrogazione, nonché per controllare la qualità dell'approssimazione di una ricerca KNN esatta (nota come rapporto di richiamo).

Entrambi gli algoritmi di ricerca vettoriale (Flat e HNSW) supportano un parametro opzionale. INITIAL_CAP Quando specificato, questo parametro prealloca la memoria per gli indici, con conseguente riduzione del sovraccarico di gestione della memoria e aumento delle velocità di ingestione vettoriale.

Gli algoritmi di ricerca vettoriale come HNSW potrebbero non gestire in modo efficiente l'eliminazione o la sovrascrittura dei vettori precedentemente inseriti. L'uso di queste operazioni può comportare un consumo eccessivo di memoria indicizzata e/o una riduzione della qualità dei richiami. La reindicizzazione è un metodo per ripristinare l'utilizzo ottimale della memoria e/o il richiamo.

Espressione di interrogazione di ricerca vettoriale

I comandi FT.SEARCH e FT.AGGREGATE richiedono un'espressione di interrogazione. Questa espressione è un parametro a stringa singola composto da uno o più operatori. Ogni operatore utilizza un campo dell'indice per identificare un sottoinsieme delle chiavi dell'indice. È possibile combinare più operatori utilizzando combinatori booleani e parentesi per migliorare o limitare ulteriormente il set di chiavi (o set di risultati) raccolto.

Carattere jolly

L'operatore jolly, l'asterisco ('*'), corrisponde a tutte le chiavi dell'indice.

Intervallo numerico

L'operatore di intervallo numerico ha la seguente sintassi:

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

< numeric-field-name > deve essere un campo di tipo dichiarato. NUMERIC Per impostazione predefinita, il limite è inclusivo, ma è possibile utilizzare una parentesi aperta iniziale ['('] per rendere esclusivo un limite. La ricerca per intervallo può essere convertita in un unico confronto relazionale (<, <=, >, > =) utilizzando Inf +Inf o -Inf come uno dei limiti. Indipendentemente dal formato numerico specificato (intero, a virgola fissa, a virgola mobile, infinito), il numero viene convertito in virgola mobile a 64 bit per eseguire confronti, riducendo di conseguenza la precisione.

Esempio Esempi
@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: confronta

L'operatore di confronto dei tag ha la seguente sintassi:

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

Se uno qualsiasi dei tag nell'operatore corrisponde a uno dei tag nel campo dei tag del record, il record viene incluso nel set di risultati. Il campo progettato da <tag-field-name> deve essere un campo dell'indice dichiarato con type. TAG Esempi di confronto tra tag sono:

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

Combinazioni booleane

I set di risultati di un operatore numerico o di tag possono essere combinati utilizzando la logica booleana: e/or. Le parentesi possono essere utilizzate per raggruppare gli operatori e/o modificare l'ordine di valutazione. La sintassi degli operatori logici booleani è:

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

Più termini combinati in una frase sono «and» -ed. Le frasi multiple combinate con la pipe ('|') sono «or» -ed.

Gli indici vettoriali supportano due diversi metodi di ricerca: il più vicino e l'intervallo. Una ricerca del vicino più prossimo individua un numero, K, dei vettori dell'indice che sono i più vicini al vettore fornito (di riferimento): questo è chiamato colloquialmente KNN per «K» dei vicini più vicini. La sintassi per una ricerca KNN è:

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

Una ricerca vettoriale KNN viene applicata solo ai vettori che soddisfano il <expression> che può essere una qualsiasi combinazione degli operatori sopra definiti: wildcard, range search, tag search e/o relative combinazioni booleane.

  • <k>è un numero intero che specifica il numero di vettori vicini più prossimi da restituire.

  • <vector-field-name>deve specificare un campo di tipo dichiarato. VECTOR

  • <parameter-name>field specifica una delle voci della PARAM tabella del FT.AGGREGATE comando FT.SEARCH or. Questo parametro è il valore vettoriale di riferimento per il calcolo della distanza. Il valore del vettore è codificato nel PARAM valore in formato binario IEEE 754 di little-endian (stessa codifica utilizzata per un campo vettoriale HASH)

  • Per gli indici vettoriali di tipo HNSW, la EF_RUNTIME clausola opzionale può essere utilizzata per sovrascrivere il valore predefinito del parametro stabilito al momento della creazione dell'indice. EF_RUNTIME

  • L'opzione <distance-field-name> fornisce un nome di campo per il set di risultati che contiene la distanza calcolata tra il vettore di riferimento e la chiave individuata.

Una ricerca per intervallo individua tutti i vettori entro una distanza (raggio) specificata da un vettore di riferimento. La sintassi per una ricerca per intervallo è:

<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> ]

Dove:

  • <vector-field-name>è il nome del campo vettoriale da cercare.

  • <radius> or $<radius-parameter>è il limite numerico di distanza per la ricerca.

  • $<reference-vector-parameter> è il nome del parametro che contiene il vettore di riferimento. Il valore del vettore è codificato nel valore PARAM in formato binario IEEE 754 di little-endian (stessa codifica utilizzata per un campo vettoriale HASH)

  • L'opzione <distance-field-name> fornisce un nome di campo per il set di risultati che contiene la distanza calcolata tra il vettore di riferimento e ciascuna chiave.

  • L'opzione opzionale <epsilon-value> controlla il limite dell'operazione di ricerca, i vettori all'interno della distanza <radius> * (1.0 + <epsilon-value>) vengono attraversati alla ricerca di risultati candidati. L'impostazione predefinita è .01.

Comando INFO

La ricerca vettoriale amplia il comando Redis OSS INFO con diverse sezioni aggiuntive di statistiche e contatori. Una richiesta di recupero della sezione SEARCH recupererà tutte le seguenti sezioni:

Sezione search_memory

Nome Descrizione
search_used_memory_bytes Numero di byte di memoria consumati in tutte le strutture di dati di ricerca
search_used_memory_human Versione leggibile dall'uomo di cui sopra

Sezione search_index_stats

Nome Descrizione
numero_di_indici_di ricerca Numero di indici creati
search_num_fulltext_indexes Numero di campi non vettoriali in tutti gli indici
search_num_vector_indexes Numero di campi vettoriali in tutti gli indici
search_num_hash_indexes Numero di indici su chiavi di tipo HASH
search_num_json_indexes Numero di indici su chiavi di tipo JSON
search_total_indexed_keys Numero totale di chiavi in tutti gli indici
search_total_indexed_vectors Numero totale di vettori in tutti gli indici
search_total_indexed_hash_keys Numero totale di chiavi di tipo HASH in tutti gli indici
search_total_indexed_json_keys Numero totale di chiavi di tipo JSON in tutti gli indici
search_total_index_size Byte utilizzati da tutti gli indici
search_total_fulltext_index_size Byte utilizzati da strutture indicizzate non vettoriali
search_total_vector_index_size Byte utilizzati dalle strutture degli indici vettoriali
search_max_index_lag_ms Ritardo di inserimento durante l'ultimo aggiornamento del batch di importazione

Sezione search_ingestion

Nome Descrizione
search_background_indexing_status Stato di ingestione. NO_ACTIVITYsignifica inattivo. Altri valori indicano che ci sono chiavi in fase di ingestione.
search_ingestion_paused Tranne durante il riavvio, questo dovrebbe sempre essere «no».

Sezione search_backfill

Nota

Alcuni dei campi documentati in questa sezione sono visibili solo quando è attualmente in corso un riempimento.

Nome Descrizione
search_num_active_backfills Numero di attività di riempimento correnti
search_backfills_paused Tranne quando la memoria è esaurita, dovrebbe sempre essere «no».
search_current_backfill_progress_percentage % di completamento (0-100) dell'attuale riempimento

Sezione search_query

Nome Descrizione
search_num_active_queries Numero di comandi and attualmente in corso FT.SEARCH FT.AGGREGATE

Sicurezza della ricerca vettoriale

I meccanismi di sicurezza Redis OSS ACL (Access Control Lists) per l'accesso ai comandi e ai dati sono estesi per controllare la funzione di ricerca. Il controllo ACL dei singoli comandi di ricerca è completamente supportato. Viene fornita una nuova categoria ACL e molte delle categorie esistenti (@fast, @read@write, ecc.) vengono aggiornate per includere i nuovi comandi. @search I comandi di ricerca non modificano i dati chiave, il che significa che il meccanismo ACL esistente per l'accesso in scrittura viene preservato. Le regole di accesso per le operazioni HASH e JSON non vengono modificate dalla presenza di un indice; a tali comandi viene comunque applicato il normale controllo dell'accesso a livello di chiave.

L'accesso ai comandi di ricerca con un indice è inoltre controllato tramite Redis OSS ACL. I controlli di accesso vengono eseguiti a livello dell'intero indice, non a livello di chiave. Ciò significa che l'accesso a un indice viene concesso a un utente solo se tale utente è autorizzato ad accedere a tutte le chiavi possibili all'interno dell'elenco dei prefissi dello spazio chiave di quell'indice. In altre parole, il contenuto effettivo di un indice non controlla l'accesso. Piuttosto, sono i contenuti teorici di un indice, come definito dall'elenco dei prefissi, che viene utilizzato per il controllo di sicurezza. Può essere facile creare una situazione in cui un utente abbia accesso in lettura e/o scrittura a una chiave ma non sia in grado di accedere a un indice contenente quella chiave. Tieni presente che per creare o utilizzare un indice è necessario solo l'accesso in lettura allo spazio delle chiavi: la presenza o l'assenza di accesso in scrittura non viene considerata.

Per ulteriori informazioni sull'utilizzo degli ACL con MemoryDB, consultate Authenticating users with Access Control Lists (ACL).