Ottimizzare il rendimento delle query vettoriali

Seleziona una versione della documentazione:

Questo documento mostra come ottimizzare gli indici per ottenere prestazioni delle query più rapide e un migliore richiamo.

Ottimizzare un indice ScaNN

L'indice ScaNN utilizza l'indicizzazione basata sulla quantizzazione ad albero. Nelle tecniche di quantizzazione ad albero, gli indici apprendono un albero di ricerca insieme a una funzione di quantizzazione (o hashing). Quando esegui una query, l'albero di ricerca viene utilizzato per ridurre lo spazio di ricerca, mentre la quantizzazione viene utilizzata per comprimere le dimensioni dell'indice. Questa potatura accelera il calcolo della somiglianza (ovvero della distanza) tra il vettore della query e i vettori del database.

Per ottenere una frequenza di query al secondo (QPS) elevata e un richiamo elevato con le query dei vicini più prossimi, devi partizionare l'albero dell'indice ScaNN nel modo più appropriato per i tuoi dati e le tue query.

Prima di creare un indice ScaNN, completa le seguenti operazioni:

  • Assicurati che sia già stata creata una tabella con i tuoi dati.
  • Assicurati che il valore impostato per il flag maintenance_work_mem e shared_buffers sia inferiore alla memoria totale della macchina per evitare problemi durante la generazione dell'indice.

Parametri di correzione

I seguenti parametri di indice e flag di database vengono utilizzati insieme per trovare il giusto equilibrio tra richiamo e QPS. Tutti i parametri si applicano a entrambi i tipi di indice ScaNN.

Parametro di ottimizzazione Descrizione Tipo di parametro
num_leaves Il numero di partizioni da applicare a questo indice. Il numero di partizioni a cui applichi un indice al momento della creazione influisce sulle prestazioni dell'indice. Aumentando le partizioni per un numero fisso di vettori, crei un indice più granulare, che migliora il richiamo e le prestazioni delle query. Tuttavia, questo comporta tempi di creazione dell'indice più lunghi.

Poiché gli alberi a tre livelli vengono creati più velocemente di quelli a due livelli, puoi aumentare il valore di num_leaves_value durante la creazione di un indice ad albero a tre livelli per ottenere prestazioni migliori.
  • Indice a due livelli: imposta questo valore su un valore compreso tra 1 e 1048576.

    Se non sai quale valore esatto selezionare, utilizza sqrt(ROWS) come punto di partenza, dove ROWS è il numero di righe del vettore. Il numero di vettori contenuti in ogni partizione viene calcolato in base a
    ROWS/sqrt(ROWS) = sqrt(ROWS).

    Poiché è possibile creare un indice ad albero a due livelli su un set di dati con meno di 10 milioni di righe di vettori, ogni partizione conterrà meno di (sqrt(10M)) vettori, ovvero 3200 vettori. Per prestazioni ottimali, è consigliabile ridurre al minimo il numero di vettori in ogni partizione.
  • Indice a tre livelli: imposta questo valore su un valore compreso tra 1 e 1048576.

    Se non sai quale valore esatto selezionare, utilizza power(ROWS, 2/3) come punto di partenza, dove ROWS è il numero di righe del vettore. Il numero di vettori contenuti in ogni partizione viene calcolato in base a
    ROWS/power(ROWS, 2/3) = power(ROWS, 1/3).

    Poiché è possibile creare un indice ad albero a tre livelli su un set di dati con più di 100 milioni di righe vettoriali, ogni partizione conterrà più di
    (power(100M, 1/3)) vettori, ovvero 465 vettori. Per prestazioni ottimali, è consigliabile ridurre al minimo il numero di vettori in ogni partizione.
Creazione dell'indice
quantizer Il tipo di quantizzatore che vuoi utilizzare per l'albero K-means. Il valore predefinito è SQ8 per migliorare il rendimento delle query.

Imposta il valore su FLAT per un migliore recupero.
Creazione dell'indice
enable_pca Attiva l'analisi delle componenti principali (PCA), una tecnica di riduzione delle dimensioni utilizzata per ridurre automaticamente le dimensioni dell'incorporamento quando possibile. Questa opzione è attivata per impostazione predefinita.

Imposta su false se noti un peggioramento del richiamo.
Creazione dell'indice
scann.num_leaves_to_search Il flag del database controlla il compromesso tra richiamo e QPS. Il valore predefinito è l'1% del valore impostato in num_leaves.

Più alto è il valore impostato, migliore è il richiamo, ma si traduce in un QPS inferiore e viceversa.
Tempo di esecuzione della query
scann.max_top_neighbors_buffer_size Il flag del database specifica le dimensioni della cache utilizzata per migliorare le prestazioni delle query filtrate assegnando un punteggio o classificando i vicini candidati scansionati in memoria anziché sul disco. Il valore predefinito è 20000.

Più alto è il valore impostato, migliore è il QPS nelle query filtrate, ma comporta un maggiore utilizzo della memoria e viceversa.
Tempo di esecuzione della query
scann.pre_reordering_num_neighbors Il flag del database, se impostato, specifica il numero di vicini candidati da considerare durante le fasi di riordinamento dopo che la ricerca iniziale identifica un insieme di candidati. Imposta questo valore su un valore superiore al numero di vicini che vuoi che la query restituisca.

Set di valori più elevati comportano un richiamo migliore, ma questo approccio comporta un QPS inferiore.
Tempo di esecuzione della query
max_num_levels Il numero massimo di livelli dell'albero di clustering K-means.
  • Indice ad albero a due livelli: impostato per impostazione predefinita per la quantizzazione basata su albero a due livelli.
  • Indice ad albero a tre livelli: impostalo su 2 in modo esplicito per la quantizzazione basata su alberi a tre livelli.
Creazione dell'indice

Ottimizzare un indice ScaNN

Prendi in considerazione i seguenti esempi per gli indici ScaNN a due e tre livelli che mostrano come vengono impostati i parametri di ottimizzazione:

Indice a due livelli

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

Indice a tre livelli

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);

Qualsiasi operazione di inserimento o aggiornamento su una tabella in cui è già stato generato un indice ScaNN influisce sul modo in cui l'albero appreso ottimizza l'indice. Se la tua tabella è soggetta a aggiornamenti o inserimenti frequenti, ti consigliamo di reindicizzare periodicamente l'indice ScaNN esistente per migliorare l'accuratezza del recupero.

Puoi monitorare le metriche dell'indice per determinare il numero di mutazioni create dalla creazione dell'indice e poi eseguire nuovamente l'indicizzazione di conseguenza. Per maggiori informazioni sulle metriche, consulta Metriche dell'indice vettoriale.

Best practice per l'ottimizzazione

A seconda del tipo di indice ScaNN che prevedi di utilizzare, i suggerimenti per ottimizzare l'indice variano. Questa sezione fornisce consigli su come ottimizzare i parametri dell'indice per ottenere un equilibrio ottimale tra richiamo e QPS.

Indice ad albero a due livelli

Per applicare i consigli che ti aiutano a trovare i valori ottimali di num_leaves e num_leaves_to_search per il tuo set di dati, segui questi passaggi:

  1. Crea l'indice ScaNN con num_leaves impostato sulla radice quadrata del conteggio delle righe della tabella indicizzata.
  2. Esegui le query di test, aumentando il valore di scann.num_of_leaves_to_search, finché non raggiungi l'intervallo di richiamo target, ad esempio il 95%. Per ulteriori informazioni sull'analisi delle query, vedi Analizzare le query.
  3. Prendi nota del rapporto tra scann.num_leaves_to_search e num_leaves che verrà utilizzato nei passaggi successivi. Questo rapporto fornisce un'approssimazione del set di dati che ti aiuterà a raggiungere il richiamo target.

    Se lavori con vettori di dimensioni elevate (500 dimensioni o più) e vuoi migliorare il richiamo, prova a modificare il valore di scann.pre_reordering_num_neighbors. Come punto di partenza, imposta il valore su 100 * sqrt(K), dove K è il limite impostato nella query.
  4. Se il tuo QPS è troppo basso dopo che le query raggiungono un richiamo target, segui questi passaggi:
    1. Ricrea l'indice, aumentando il valore di num_leaves e scann.num_leaves_to_search in base alle seguenti indicazioni:
      • Imposta num_leaves su un fattore maggiore della radice quadrata del numero di righe. Ad esempio, se l'indice ha num_leaves impostato sulla radice quadrata del conteggio delle righe, prova a impostarlo sul doppio della radice quadrata. Se il valore è già il doppio, prova a impostarlo sul triplo della radice quadrata.
      • Aumenta scann.num_leaves_to_search in base alle necessità per mantenere il rapporto con num_leaves, che hai annotato nel passaggio 3.
      • Imposta num_leaves su un valore minore o uguale al numero di righe diviso per 100.
    2. Esegui di nuovo le query di test. Mentre esegui le query di test, prova a ridurre scann.num_leaves_to_search, trovando un valore che aumenti le QPS mantenendo un richiamo elevato. Prova valori diversi di scann.num_leaves_to_search senza ricompilare l'indice.
  5. Ripeti il passaggio 4 finché sia il QPS sia l'intervallo di richiamo non avranno raggiunto valori accettabili.

Indice ad albero a tre livelli

Oltre ai consigli per l'indice dell'albero a due livelli ScaNN, utilizza le seguenti indicazioni e i passaggi per ottimizzare l'indice:

  • Aumentare il valore di max_num_levels da 1 per un albero a due livelli a 2 per un albero a tre livelli riduce significativamente il tempo necessario per creare un indice, ma a scapito dell'accuratezza del richiamo. Imposta max_num_levels utilizzando il seguente consiglio:
    • Imposta il valore su 2 se il numero di righe del vettore supera i 100 milioni.
    • Imposta il valore su 1 se il numero di righe del vettore è inferiore a 10 milioni.
    • Imposta 1 o 2 se il numero di righe vettoriali è compreso tra 10 milioni e 100 milioni di righe, in base al bilanciamento del tempo di creazione dell'indice e alla precisione del recupero che ti serve.

Per applicare i consigli per trovare il valore ottimale dei parametri di indice num_leaves e max_num_levels:

  1. Crea l'indice ScaNN con le seguenti combinazioni di num_leaves e max_num_levels in base al tuo set di dati:

    • Righe vettoriali superiori a 100 milioni di righe: imposta max_num_levels come 2 e num_leaves come power(rows, ⅔).
    • Righe vettoriali inferiori a 100 milioni di righe: imposta max_num_levels come 1 e num_leaves come sqrt(rows).
    • Righe di vettori tra 10 milioni e 100 milioni di righe: inizia impostando max_num_levels come 1 e num_leaves come sqrt(rows).
  2. Esegui le query di test. Per maggiori informazioni sull'analisi delle query, vedi Analizzare le query.

    Se il tempo di creazione dell'indice è soddisfacente, mantieni il valore max_num_levels e prova con il valore num_leaves per un'accuratezza ottimale del recupero.

  3. Se non sei soddisfatto del tempo di creazione dell'indice, procedi nel seguente modo:

    • Se il valore di max_num_levels è 1, elimina l'indice. Ricostruisci l'indice con il valore max_num_levels impostato su 2.

      Esegui le query e regola il valore di num_leaves per un'accuratezza ottimale del richiamo.

    • Se il valore max_num_levels è 2, elimina l'indice. Ricostruisci l'indice con lo stesso valore max_num_levels e ottimizza il valore num_leaves per una precisione di recupero ottimale.

Ottimizzare un indice IVF

La regolazione dei valori impostati per i parametri lists, ivf.probes e quantizer potrebbe contribuire a ottimizzare il rendimento dell'applicazione:

Parametro di ottimizzazione Descrizione Tipo di parametro
lists Il numero di elenchi creati durante la creazione dell'indice. Il punto di partenza per impostare questo valore è (rows)/1000 per un massimo di un milione di righe e sqrt(rows) per più di un milione di righe. Creazione dell'indice
quantizer Il tipo di quantizzatore che vuoi utilizzare per l'albero K-means. Il valore predefinito è SQ8 per migliorare il rendimento delle query. Imposta il valore su FLAT per un migliore ricordo. Creazione dell'indice
ivf.probes il numero di elenchi più vicini da esplorare durante la ricerca. Il punto di partenza per questo valore è
sqrt(lists).
Tempo di esecuzione della query

Considera l'esempio seguente che mostra un indice IVF con i parametri di ottimizzazione impostati:

SET LOCAL ivf.probes = 10;

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

Ottimizzare un indice IVFFlat

La regolazione dei valori impostati per i parametri lists e ivfflat.probes può contribuire a ottimizzare il rendimento dell'applicazione:

Parametro di ottimizzazione Descrizione Tipo di parametro
lists Il numero di elenchi creati durante la creazione dell'indice. Il punto di partenza per impostare questo valore è (rows)/1000 per un massimo di un milione di righe e sqrt(rows) per più di un milione di righe. Creazione dell'indice
ivfflat.probes Il numero di elenchi più vicini da esplorare durante la ricerca. Il punto di partenza per questo valore è
sqrt(lists).
Tempo di esecuzione della query

Prima di creare un indice IVFFlat, assicurati che il flag max_parallel_maintenance_workers del tuo database sia impostato su un valore sufficiente ad accelerare la creazione dell'indice nelle tabelle di grandi dimensioni.

Considera l'esempio seguente che mostra un indice IVFFlat con i parametri di ottimizzazione impostati:

SET LOCAL ivfflat.probes = 10;

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

Ottimizzare un indice HNSW

La regolazione dei valori impostati per i parametri m, ef_construction e hnsw.ef_search può contribuire a ottimizzare il rendimento dell'applicazione.

Parametro di ottimizzazione Descrizione Tipo di parametro
m Il numero massimo di connessioni per nodo nel grafico. Puoi iniziare con il valore predefinito 16(impostazione predefinita) e sperimentare con valori più alti in base alle dimensioni del set di dati. Creazione dell'indice
ef_construction La dimensione dell'elenco dinamico di candidati mantenuto durante la costruzione del grafico, che aggiorna costantemente i migliori candidati attuali per i vicini più prossimi di un nodo. Imposta questo valore su un valore superiore al doppio del valore di m, ad esempio 64(valore predefinito). Creazione dell'indice
ef_search La dimensione dell'elenco dinamico di candidati utilizzato durante la ricerca. Puoi iniziare a impostare questo valore su m o ef_construction e poi modificarlo osservando il richiamo. Il valore predefinito è 40. Tempo di esecuzione della query

Considera l'esempio seguente che mostra un indice hnsw con i parametri di ottimizzazione impostati:

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);

Analizzare le query

Utilizza il comando EXPLAIN ANALYZE per analizzare gli approfondimenti sulle query come mostrato nella seguente query SQL di esempio.

  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;

La risposta di esempio QUERY PLAN include informazioni quali il tempo impiegato, il numero di righe scansionate o restituite e le risorse utilizzate.

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

Visualizzare le metriche dell'indice vettoriale

Puoi utilizzare le metriche dell'indice vettoriale per esaminare il rendimento dell'indice vettoriale, identificare le aree da migliorare e ottimizzare l'indice in base alle metriche, se necessario.

Per visualizzare tutte le metriche dell'indice vettoriale, esegui la seguente query SQL, che utilizza la vista pg_stat_ann_indexes:

SELECT * FROM pg_stat_ann_indexes;

Per saperne di più sull'elenco completo delle metriche, consulta Metriche dell'indice vettoriale.

Passaggi successivi