Gestire gli indici vettoriali

Questo documento descrive come creare e gestire gli indici vettoriali per accelerare le ricerche vettoriali.

Un indice vettoriale è una struttura di dati progettata per consentire alla funzione VECTOR_SEARCH di essere eseguita in modo più efficiente, soprattutto su grandi set di dati. Quando utilizzi un indice, VECTOR_SEARCH utilizza gli algoritmi Approximate Nearest Neighbor (ANN) per ridurre la latenza delle query e il costo di calcolo. Sebbene ANN introduca un certo grado di approssimazione, il che significa che il richiamo potrebbe non essere del 100%, i miglioramenti del rendimento in genere offrono un vantaggio per la maggior parte delle applicazioni.

Ruoli e autorizzazioni

Per creare un indice vettoriale, devi disporre dell'autorizzazione IAM bigquery.tables.createIndex per la tabella in cui stai creando l'indice. Per eliminare un indice vettoriale, devi disporre dell'autorizzazione bigquery.tables.deleteIndex. Ciascuno dei seguenti ruoli IAM predefiniti include le autorizzazioni necessarie per lavorare con gli indici vettoriali:

  • BigQuery Data Owner (roles/bigquery.dataOwner)
  • Editor dati BigQuery (roles/bigquery.dataEditor)

Scegli un tipo di indice vettoriale

BigQuery offre due tipi di indice vettoriale, IVF e TreeAH, ognuno dei quali supporta casi d'uso diversi. BigQuery supporta il batch per la ricerca vettoriale elaborando più righe dei dati di input nella funzione VECTOR_SEARCH. Per i piccoli batch di query, sono preferibili gli indici IVF. Per i batch di query di grandi dimensioni, sono preferibili gli indici TreeAH, creati con l'algoritmo ScaNN di Google.

Indice IVF

IVF è un indice di file invertito che utilizza un algoritmo k-means per raggruppare i dati vettoriali e quindi partizionarli in base a questi cluster. La funzione VECTOR_SEARCH può utilizzare queste partizioni per ridurre la quantità di dati da leggere per determinare un risultato.

Indice TreeAH

Il tipo di indice TreeAH prende il nome dalla combinazione di una struttura ad albero e dall'utilizzo dell'hashing asimmetrico (AH), una tecnica di quantizzazione di base dell'algoritmo ScaNN sottostante. Un indice TreeAH funziona nel seguente modo:

  1. La tabella di base è suddivisa in shard più piccoli e più gestibili.
  2. Viene addestrato un modello di clustering, con il numero di cluster derivato dall'opzione leaf_node_embedding_count nell'argomento tree_ah_options dell'istruzione CREATE VECTOR INDEX.
  3. I vettori vengono compressi con la quantizzazione del prodotto, una tecnica che riduce l'utilizzo della memoria. I vettori compressi vengono quindi archiviati nelle tabelle dell'indice anziché nei vettori originali, riducendo così le dimensioni dell'indice vettoriale.
  4. Quando viene eseguita la funzione VECTOR_SEARCH, viene calcolato in modo efficiente un elenco di candidati per ogni vettore di query utilizzando l'hashing asimmetrico, ottimizzato a livello hardware per calcoli di distanza approssimativi. Questi candidati vengono quindi riassegnati e riordinati utilizzando gli incorporamenti esatti.

L'algoritmo TreeAH è ottimizzato per le query batch che elaborano centinaia o più vettori di query. L'utilizzo della quantizzazione del prodotto può ridurre in modo significativo la latenza e il costo, potenzialmente di ordini di grandezza rispetto a IVF. Tuttavia, a causa dell'aumento dell'overhead, l'algoritmo IVF potrebbe essere più adatto quando hai un numero inferiore di vettori di query.

Ti suggeriamo di provare il tipo di indice TreeAH se il tuo caso d'uso soddisfa i seguenti criteri:

  • La tabella contiene un massimo di 200 milioni di righe.

  • Esegui spesso query batch di grandi dimensioni che coinvolgono centinaia o più vettori di query.

Per le query di piccoli batch con il tipo di indice TreeAH, VECTOR_SEARCH potrebbe tornare alla ricerca esaustiva. In questo caso, viene fornito un IndexUnusedReason per spiegare perché l'indice vettoriale non è stato utilizzato.

Crea un indice vettoriale IVF

Per creare un indice vettoriale IVF, utilizza l'istruzione DDL (Data Definition Language) CREATE VECTOR INDEX:

  1. Vai alla pagina BigQuery.

    Vai a BigQuery

  2. Nell'editor di query, esegui la seguente istruzione SQL:

    Per creare un indice vettoriale IVF:

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

    Sostituisci quanto segue:

    • INDEX_NAME: il nome dell'indice vettoriale che stai creando. Poiché l'indice viene sempre creato nello stesso progetto e set di dati della tabella di base, non è necessario specificarli nel nome.
    • DATASET_NAME: il nome del set di dati che contiene la tabella.
    • TABLE_NAME: il nome della tabella che contiene la colonna con i dati degli incorporamenti.
    • COLUMN_NAME: il nome di una colonna che contiene i dati degli incorporamenti. La colonna deve essere di tipo ARRAY<FLOAT64>. La colonna non può contenere campi secondari. Tutti gli elementi dell'array devono essere non-NULL e tutti i valori della colonna devono avere le stesse dimensioni dell'array.
    • STORED_COLUMN_NAME: il nome di una colonna di primo livello nella tabella da archiviare nell'indice vettoriale. Il tipo di colonna non può essere RANGE. Le colonne archiviate non vengono utilizzate se la tabella ha una policy di accesso a livello di riga o se la colonna ha un tag di criteri. Per informazioni su come attivare le colonne archiviate, consulta Archiviazione e prefiltraggio delle colonne.
    • DISTANCE_TYPE: specifica il tipo di distanza predefinito da utilizzare quando si esegue una ricerca vettoriale utilizzando questo indice. I valori supportati sono EUCLIDEAN, COSINE e DOT_PRODUCT. EUCLIDEAN è il valore predefinito.

      La creazione dell'indice utilizza sempre la distanza EUCLIDEAN per l'addestramento, ma la distanza utilizzata nella funzione VECTOR_SEARCH può essere diversa.

      Se specifichi un valore per l'argomento distance_type della funzione VECTOR_SEARCH, questo valore viene utilizzato al posto del valore DISTANCE_TYPE.

    • NUM_LISTS: un valore INT64 che specifica il numero di elenchi in cui il cluster dell'indice IVF e in cui vengono partizionati i dati vettoriali. Questo valore deve essere pari o inferiore a 5000. Durante l'indicizzazione, i vettori vengono assegnati all'elenco corrispondente al centroide del cluster più vicino. Se ometti questo argomento, BigQuery determina un valore predefinito in base alle caratteristiche dei dati. Il valore predefinito funziona bene per la maggior parte dei casi d'uso.

      NUM_LISTS controlla la granularità dell'ottimizzazione delle query. Valori più elevati creano più elenchi, quindi puoi impostare l'opzione fraction_lists_to_search della funzione VECTOR_SEARCH per analizzare una percentuale inferiore dell'indice. Ad esempio, la scansione dell'1% di 100 elenchi anziché la scansione del 10% di 10 elenchi. Ciò consente un controllo più preciso della velocità di ricerca e del richiamo, ma aumenta leggermente il costo di indicizzazione. Imposta il valore di questo argomento in base alla precisione con cui devi regolare l'ambito della query.

L'esempio seguente crea un indice vettoriale sulla colonna embedding di 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');

Il seguente esempio crea un indice vettoriale nella colonna embedding di my_table e specifica il tipo di distanza da utilizzare e le opzioni 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}')

Crea un indice vettoriale TreeAH

Per creare un indice vettoriale TreeAH, utilizza l'istruzione DDL (Data Definition Language) CREATE VECTOR INDEX:

  1. Vai alla pagina BigQuery.

    Vai a BigQuery

  2. Nell'editor di query, esegui la seguente istruzione SQL:

    CREATE [ OR REPLACE ] VECTOR INDEX [ IF NOT EXISTS ] INDEX_NAME
    ON DATASET_NAME.TABLE_NAME(COLUMN_NAME)
    STORING(STORED_COLUMN_NAME [, ...])
    OPTIONS(index_type = 'TREE_AH',
      distance_type = 'DISTANCE_TYPE',
      tree_ah_options = '{"leaf_node_embedding_count":LEAF_NODE_EMBEDDING_COUNT,
        "normalization_type":"NORMALIZATION_TYPE"}')

    Sostituisci quanto segue:

    • INDEX_NAME: il nome dell'indice vettoriale che stai creando. Poiché l'indice viene sempre creato nello stesso progetto e set di dati della tabella di base, non è necessario specificarli nel nome.
    • DATASET_NAME: il nome del set di dati che contiene la tabella.
    • TABLE_NAME: il nome della tabella che contiene la colonna con i dati degli incorporamenti.
    • COLUMN_NAME: il nome di una colonna che contiene i dati degli incorporamenti. La colonna deve essere di tipo ARRAY<FLOAT64>. La colonna non può contenere campi secondari. Tutti gli elementi dell'array devono essere non-NULL e tutti i valori della colonna devono avere le stesse dimensioni dell'array.
    • STORED_COLUMN_NAME: il nome di una colonna di primo livello nella tabella da archiviare nell'indice vettoriale. Il tipo di colonna non può essere RANGE. Le colonne archiviate non vengono utilizzate se la tabella ha una policy di accesso a livello di riga o se la colonna ha un tag di criteri. Per informazioni su come attivare le colonne archiviate, consulta Archiviazione e prefiltraggio delle colonne.
    • DISTANCE_TYPE: un argomento facoltativo che specifica il tipo di distanza predefinito da utilizzare quando si esegue una ricerca vettoriale utilizzando questo indice. I valori supportati sono EUCLIDEAN, COSINE e DOT_PRODUCT. EUCLIDEAN è il valore predefinito.

      La creazione dell'indice utilizza sempre la distanza EUCLIDEAN per l'addestramento, ma la distanza utilizzata nella funzione VECTOR_SEARCH può essere diversa.

      Se specifichi un valore per l'argomento distance_type della funzione VECTOR_SEARCH, questo valore viene utilizzato al posto del valore DISTANCE_TYPE.

    • LEAF_NODE_EMBEDDING_COUNT: un valore INT64 maggiore o uguale a 500 che specifica il numero approssimativo di vettori in ogni nodo foglia dell'albero creato dall'algoritmo TreeAH. L'algoritmo TreeAH divide l'intero spazio di dati in un numero di elenchi, ognuno dei quali contiene circa LEAF_NODE_EMBEDDING_COUNT punti dati. Un valore inferiore crea più elenchi con meno punti dati, mentre un valore maggiore crea meno elenchi con più punti dati. Il valore predefinito è 1000, che è appropriato per la maggior parte dei set di dati.

    • NORMALIZATION_TYPE: un valore STRING. I valori supportati sono NONE o L2. Il valore predefinito è NONE. La normalizzazione avviene prima di qualsiasi elaborazione, sia per i dati della tabella di base sia per i dati della query, ma non modifica la colonna di incorporamento COLUMN_NAME in TABLE_NAME. A seconda del set di dati, del modello di incorporamento e del tipo di distanza utilizzato durante VECTOR_SEARCH, la normalizzazione degli incorporamenti potrebbe migliorare il richiamo.

L'esempio seguente crea un indice vettoriale nella colonna embedding di my_table e specifica il tipo di distanza da utilizzare e le opzioni TreeAH:

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

CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
OPTIONS (index_type = 'TREE_AH', distance_type = 'EUCLIDEAN',
tree_ah_options = '{"normalization_type": "L2"}');

Filtri

Le sezioni seguenti spiegano in che modo i prefiltri e i postfiltri influiscono sui risultati della ricerca vettoriale e come eseguire il prefiltraggio utilizzando le colonne e le partizioni archiviate nell'indice vettoriale.

Prefiltri e post-filtri

Nelle operazioni VECTOR_SEARCH di BigQuery, sia il pre-filtraggio che il post-filtraggio servono a perfezionare i risultati di ricerca, applicando condizioni basate sulle colonne di metadati associate agli incorporamenti vettoriali. È importante comprendere le differenze, l'implementazione e l'impatto per ottimizzare le prestazioni, il costo e l'accuratezza delle query.

Il prefiltraggio e il post-filtraggio sono definiti come segue:

  • Pre-filtraggio:applica le condizioni di filtro prima che la ricerca approssimativa del vicino più prossimo (ANN) esegua i calcoli della distanza sui vettori candidati. In questo modo, si restringe il pool di vettori presi in considerazione durante la ricerca. Di conseguenza, il pre-filtraggio spesso comporta tempi di query più rapidi e costi di calcolo ridotti, in quanto la ricerca ANN valuta un numero inferiore di potenziali candidati.
  • Post-filtraggio:applica le condizioni del filtro dopo che la ricerca ANN ha identificato i top_k vicini più prossimi iniziali. In questo modo il set di risultati finale viene perfezionato in base ai criteri specificati.

Il posizionamento della clausola WHERE determina se un filtro funge da pre-filtro o post-filtro.

Per creare un prefiltro, la clausola WHERE della query deve essere applicata all'argomento della tabella di base della funzione VECTOR_SEARCH. Il predicato deve essere applicato a una colonna memorizzata, altrimenti diventa un post-filtro.

Il seguente esempio mostra come creare un prefiltro:

-- Pre-filter on a stored column. The index speeds up the query.
SELECT *
FROM
  VECTOR_SEARCH(
    (SELECT * FROM my_dataset.my_table WHERE type = 'animal'),
    'embedding',
    TABLE my_dataset.my_testdata);

-- Filter on a column that isn't stored. The index is used to search the
-- entire table, and then the results are post-filtered. You might see fewer
-- than 5 matches returned for some embeddings.
SELECT query.test_id, base.type, distance
FROM
  VECTOR_SEARCH(
    (SELECT * FROM my_dataset.my_table WHERE id = 123),
    'embedding',
    TABLE my_dataset.my_testdata,
    top_k => 5);

-- Use pre-filters with brute force. The data is filtered and then searched
-- with brute force for exact results.
SELECT query.test_id, base.type, distance
FROM
  VECTOR_SEARCH(
    (SELECT * FROM my_dataset.my_table WHERE id = 123),
    'embedding',
    TABLE my_dataset.my_testdata,
    options => '{"use_brute_force":true}');

Per creare un post-filtro, la clausola WHERE della query deve essere applicata al di fuori della funzione VECTOR_SEARCH, in modo che filtri i risultati restituiti dalla ricerca.

Il seguente esempio mostra come creare un post-filtro:

-- Use post-filters. The index is used, but the entire table is searched and
-- the post-filtering might reduce the number of results.
SELECT query.test_id, base.type, distance
FROM
  VECTOR_SEARCH(
    TABLE my_dataset.my_table,
    'embedding',
    TABLE my_dataset.my_testdata,
    top_k => 5)
WHERE base.type = 'animal';

SELECT base.id, distance
FROM
  VECTOR_SEARCH(
    TABLE mydataset.base_table,
    'embedding',
    (SELECT embedding FROM mydataset.query_table),
    top_k => 10
  )
WHERE type = 'document' AND year > 2022

Quando utilizzi il post-filtraggio o quando i filtri della tabella di base che specifichi fanno riferimento a colonne non archiviate e quindi fungono da post-filtri, il set di risultati finale potrebbe contenere meno di top_k righe, potenzialmente anche zero righe, se il predicato è selettivo. Se hai bisogno di un numero specifico di risultati dopo il filtraggio, valuta la possibilità di specificare un valore top_k più elevato o di aumentare il valore fraction_lists_to_search nella chiamata VECTOR_SEARCH.

In alcuni casi, soprattutto se il prefiltro è molto selettivo, il prefiltraggio può anche ridurre le dimensioni del set di risultati. In questo caso, prova ad aumentare il valore di fraction_lists_to_search nella chiamata VECTOR_SEARCH.

Pre-filtrare con le colonne archiviate

Per migliorare ulteriormente l'efficienza dell'indice vettoriale, puoi specificare le colonne della tabella di base da archiviare nell'indice vettoriale. L'utilizzo delle colonne archiviate può ottimizzare le query che chiamano la funzione VECTOR_SEARCH nei seguenti modi:

  • Anziché cercare in un'intera tabella, puoi chiamare la funzione VECTOR_SEARCH in un'istruzione di query che prefiltra la tabella di base con una clausola WHERE. Se la tabella ha un indice e filtri solo sulle colonne archiviate, BigQuery ottimizza la query filtrando i dati prima della ricerca e poi utilizzando l'indice per cercare nel set di risultati più piccolo. Se filtri le colonne non archiviate, BigQuery applica il filtro dopo la ricerca nella tabella o post-filtri.

  • La funzione VECTOR_SEARCH restituisce una struct denominata base che contiene tutte le colonne della tabella di base. Senza colonne archiviate, è necessario un join potenzialmente costoso per recuperare le colonne archiviate in base. Se utilizzi un indice IVF e la query seleziona solo le colonne archiviate da base, BigQuery ottimizza la query per eliminare il join. Per gli indici TreeAH, il join con la tabella di base non viene rimosso. Le colonne memorizzate negli indici TreeAH vengono utilizzate solo per scopi di prefiltraggio.

Per memorizzare le colonne, elencale nella clausola STORING dell'istruzione CREATE VECTOR INDEX. L'archiviazione delle colonne aumenta le dimensioni dell'indice vettoriale, quindi è consigliabile archiviare solo le colonne utilizzate o filtrate più di frequente.

L'esempio seguente crea un indice vettoriale con colonne archiviate, quindi esegue una query di ricerca vettoriale che seleziona solo le colonne archiviate:

-- Create a table that contains an embedding.
CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>, type STRING, creation_time DATETIME, id INT64);

-- Create a query table that contains an embedding.
CREATE TABLE my_dataset.my_testdata(embedding ARRAY<FLOAT64>, test_id INT64);

-- Create a vector index with stored columns.
CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
STORING (type, creation_time)
OPTIONS (index_type = 'IVF');

-- Select only stored columns from a vector search to avoid an expensive join.
SELECT query, base.type, distance
FROM
  VECTOR_SEARCH(
    TABLE my_dataset.my_table,
    'embedding'
    TABLE my_dataset.my_testdata);

Limitazioni delle colonne memorizzate

  • Se la modalità, il tipo o lo schema di una colonna vengono modificati nella tabella di base e se si tratta di una colonna archiviata nell'indice vettoriale, potrebbe verificarsi un ritardo prima che la modifica venga applicata all'indice vettoriale. Finché gli aggiornamenti non vengono applicati all'indice, le query di ricerca vettoriale utilizzano le colonne archiviate modificate della tabella di base.
  • Se selezioni una colonna di tipo STRUCT dall'output query di una query VECTOR_SEARCH su una tabella con un indice con colonne archiviate, l'intera query potrebbe non riuscire.

Prefiltrare con le partizioni

Se la tabella su cui stai creando l'indice vettoriale è partizionata, puoi scegliere di partizionare anche l'indice vettoriale. Il partizionamento dell'indice vettoriale offre i seguenti vantaggi:

  • L'eliminazione delle partizioni viene applicata agli indici vettoriali oltre alle partizioni della tabella. L'eliminazione delle partizioni si verifica quando la ricerca vettoriale utilizza un filtro idoneo sul valore della colonna di partizionamento. In questo modo, BigQuery può analizzare le partizioni che corrispondono al filtro e saltare quelle rimanenti. L'eliminazione delle partizioni può ridurre i costi di I/O. Per maggiori informazioni sul pruning delle partizioni, consulta la pagina Esecuzione di query sulle tabelle partizionate.
  • È meno probabile che la ricerca vettoriale non trovi risultati se filtri in anticipo la colonna di partizionamento.

Puoi partizionare solo gli indici vettoriali TreeAH.

Il partizionamento di un indice vettoriale è consigliato solo se utilizzi il pre-filtraggio per limitare la maggior parte delle ricerche vettoriali a poche partizioni.

Per creare un indice partizionato, utilizza la clausola PARTITION BY dell'istruzione CREATE VECTOR INDEX. La clausola PARTITION BY che specifichi nell'istruzione CREATE VECTOR INDEX deve essere uguale alla clausola PARTITION BY specificata nell'istruzione CREATE TABLE della tabella su cui stai creando l'indice vettoriale, come mostrato nell'esempio seguente:

-- Create a date-partitioned table.
CREATE TABLE my_dataset.my_table(
  embeddings ARRAY
  id INT64,
  date DATE,
)
PARTITION BY date;

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX my_index ON my_dataset.my_table(embeddings)
PARTITION BY date
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Se la tabella utilizza il partizionamento delle colonne per intervallo di numeri interi o unità di tempo, la colonna di partizionamento viene memorizzata nell'indice vettoriale, il che aumenta il costo di archiviazione. Se una colonna della tabella viene utilizzata sia nelle clausole STORING che PARTITION BY dell'istruzione CREATE VECTOR INDEX, la colonna viene archiviata una sola volta.

Per utilizzare la partizione dell'indice vettoriale, filtra la colonna di partizionamento nella query secondaria della tabella di base dell'istruzione VECTOR_SEARCH. Nell'esempio seguente, la tabella samples.items è partizionata in base alla colonna produced_date, quindi la sottoquery della tabella di base nell'istruzione VECTOR_SEARCH filtra in base alla colonna produced_date:

SELECT query.id, base.id, distance
FROM VECTOR_SEARCH(
  (SELECT * FROM my_dataset.my_table WHERE produced_date = '2025-01-01'),
  'embedding',
  TABLE samples.test,
  distance_type => 'COSINE',
  top_k => 10
);

Esempi

Crea un indice vettoriale partizionato su una tabella partizionata per data e ora:

-- Create a datetime-partitioned table.
CREATE TABLE my_dataset.my_table(
  id INT64,
  produced_date DATETIME,
  embeddings ARRAY
)
PARTITION BY produced_date;

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX index0 ON my_dataset.my_table(embeddings)
PARTITION BY produced_date
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Crea un indice vettoriale partizionato su una tabella partizionata per timestamp:

-- Create a timestamp-partitioned table.
CREATE TABLE my_dataset.my_table(
  id INT64,
  produced_time TIMESTAMP,
  embeddings ARRAY
)
PARTITION BY TIMESTAMP_TRUNC(produced_time, HOUR);

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX index0 ON my_dataset.my_table(embeddings)
PARTITION BY TIMESTAMP_TRUNC(produced_time, HOUR)
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Crea un indice vettoriale partizionato in una tabella partizionata con intervallo di numeri interi:

-- Create a integer range-partitioned table.
CREATE TABLE my_dataset.my_table(
  id INT64,
  embeddings ARRAY
)
PARTITION BY RANGE_BUCKET(id, GENERATE_ARRAY(-100, 100, 20));

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX index0 ON my_dataset.my_table(embeddings)
PARTITION BY RANGE_BUCKET(id, GENERATE_ARRAY(-100, 100, 20))
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Crea un indice vettoriale partizionato in una tabella partizionata per data di importazione:

-- Create a ingestion time-partitioned table.
CREATE TABLE my_dataset.my_table(
  id INT64,
  embeddings ARRAY
)
PARTITION BY TIMESTAMP_TRUNC(_PARTITIONTIME, DAY);

-- Create a partitioned vector index on the table.
CREATE VECTOR INDEX index0 ON my_dataset.my_table(embeddings)
PARTITION BY TIMESTAMP_TRUNC(_PARTITIONTIME, DAY)
OPTIONS(index_type='TREE_AH', distance_type='COSINE');

Limitazioni del prefiltraggio

Informazioni sull'indicizzazione dei dati

Gli indici vettoriali sono completamente gestiti da BigQuery e vengono aggiornati automaticamente quando la tabella indicizzata cambia.

L'indicizzazione è asincrona. Si verifica un ritardo tra l'aggiunta di nuove righe alla tabella di base e il momento in cui le nuove righe vengono visualizzate nell'indice. Tuttavia, la funzione VECTOR_SEARCH tiene comunque conto di tutte le righe e non perde le righe non indicizzate. La funzione esegue la ricerca utilizzando l'indice per i record indicizzati e la ricerca esaustiva per i record non ancora indicizzati.

Se crei un indice vettoriale su una tabella di dimensioni inferiori a 10 MB, l'indice vettoriale non viene compilato. Allo stesso modo, se elimini dati da una tabella indicizzata e le dimensioni della tabella scendono al di sotto di 10 MB, l'indice vettoriale viene disattivato temporaneamente. In questo caso, le query di ricerca vettoriale non utilizzano l'indice e il codice indexUnusedReasons nella sezione vectorSearchStatistics della risorsa Job è BASE_TABLE_TOO_SMALL. Senza l'indice, VECTOR_SEARCH esegue automaticamente il fallback all'utilizzo della forza bruta per trovare i vicini più prossimi degli incorporamenti.

Se elimini la colonna indicizzata in una tabella o rinomini la tabella stessa, l'indice vettoriale viene eliminato automaticamente.

Monitorare lo stato degli indici vettoriali

Puoi monitorare l'integrità degli indici vettoriali eseguendo query sulle viste INFORMATION_SCHEMA. Le seguenti viste contengono metadati sugli indici vettoriali:

  • La visualizzazione INFORMATION_SCHEMA.VECTOR_INDEXES contiene informazioni sugli indici vettoriali in un set di dati.

    Al termine dell'istruzione CREATE VECTOR INDEX, l'indice deve comunque essere compilato prima di poter essere utilizzato. Puoi utilizzare le colonne last_refresh_time e coverage_percentage per verificare la preparazione di un indice vettoriale. Se l'indice vettoriale non è pronto, puoi comunque utilizzare la funzione VECTOR_SEARCH su una tabella, ma potrebbe essere eseguita più lentamente senza l'indice.

  • La INFORMATION_SCHEMA.VECTOR_INDEX_COLUMNS vista contiene informazioni sulle colonne con indice vettoriale per tutte le tabelle di un set di dati.

  • La INFORMATION_SCHEMA.VECTOR_INDEX_OPTIONSvisualizzazione contiene informazioni sulle opzioni utilizzate dagli indici vettoriali in un set di dati.

Esempi di indici vettoriali

L'esempio seguente mostra tutti gli indici vettoriali attivi nelle tabelle del set di dati my_dataset, che si trova nel progetto my_project. Sono inclusi i nomi, le istruzioni DDL utilizzate per crearli e la percentuale di copertura. Se una tabella di base indicizzata è inferiore a 10 MB, il relativo indice non viene compilato, nel qual caso il valore di coverage_percentage è 0.

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

Il risultato è simile al seguente:

+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+
| table_name | index_name | ddl                                                                                             | coverage_percentage |
+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+
| table1     | indexa     | CREATE VECTOR INDEX `indexa` ON `my_project.my_dataset.table1`(embeddings)                      | 100                 |
|            |            | OPTIONS (distance_type = 'EUCLIDEAN', index_type = 'IVF', ivf_options = '{"num_lists": 100}')   |                     |
+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+
| table2     | indexb     | CREATE VECTOR INDEX `indexb` ON `my_project.my_dataset.table2`(vectors)                         | 42                  |
|            |            | OPTIONS (distance_type = 'COSINE', index_type = 'IVF', ivf_options = '{"num_lists": 500}')      |                     |
+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+
| table3     | indexc     | CREATE VECTOR INDEX `indexc` ON `my_project.my_dataset.table3`(vectors)                         | 98                  |
|            |            | OPTIONS (distance_type = 'DOT_PRODUCT', index_type = 'TREE_AH',                                 |                     |
|            |            |          tree_ah_options = '{"leaf_node_embedding_count": 1000, "normalization_type": "NONE"}') |                     |
+------------+------------+-------------------------------------------------------------------------------------------------+---------------------+

Esempi di colonne dell'indice vettoriale

La seguente query estrae informazioni sulle colonne che hanno indici vettoriali:

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

Il risultato è simile al seguente:

+------------+------------+-------------------+------------------+
| table_name | index_name | index_column_name | index_field_path |
+------------+------------+-------------------+------------------+
| table1     | indexa     | embeddings        | embeddings       |
| table2     | indexb     | vectors           | vectors          |
| table3     | indexc     | vectors           | vectors          |
+------------+------------+-------------------+------------------+

Esempi di opzioni dell'indice vettoriale

La seguente query estrae informazioni sulle opzioni dell'indice vettoriale:

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

Il risultato è simile al seguente:

+------------+------------+------------------+------------------+-------------------------------------------------------------------+
| table_name | index_name | option_name      | option_type      | option_value                                                      |
+------------+------------+------------------+------------------+-------------------------------------------------------------------+
| table1     | indexa     | index_type       | STRING           | IVF                                                               |
| table1     | indexa     | distance_type    | STRING           | EUCLIDEAN                                                         |
| table1     | indexa     | ivf_options      | STRING           | {"num_lists": 100}                                                |
| table2     | indexb     | index_type       | STRING           | IVF                                                               |
| table2     | indexb     | distance_type    | STRING           | COSINE                                                            |
| table2     | indexb     | ivf_options      | STRING           | {"num_lists": 500}                                                |
| table3     | indexc     | index_type       | STRING           | TREE_AH                                                           |
| table3     | indexc     | distance_type    | STRING           | DOT_PRODUCT                                                       |
| table3     | indexc     | tree_ah_options  | STRING           | {"leaf_node_embedding_count": 1000, "normalization_type": "NONE"} |
+------------+------------+------------------+------------------+-------------------------------------------------------------------+

Verifica dell'utilizzo dell'indice vettoriale

Le informazioni sull'utilizzo dell'indice vettoriale sono disponibili nei metadati del job che ha eseguito la query di ricerca vettoriale. Puoi visualizzare i metadati del job utilizzando la console Google Cloud , lo strumento a riga di comando bq, l'API BigQuery o le librerie client.

Quando utilizzi la console Google Cloud , puoi trovare informazioni sull'utilizzo dell'indice vettoriale nei campi Modalità di utilizzo dell'indice vettoriale e Motivi del mancato utilizzo dell'indice vettoriale.

Quando utilizzi lo strumento bq o l'API BigQuery, puoi trovare informazioni sull'utilizzo dell'indice vettoriale nella sezione VectorSearchStatistics della risorsa Job.

La modalità di utilizzo dell'indice indica se è stato utilizzato un indice vettoriale fornendo uno dei seguenti valori:

  • UNUSED: non è stato utilizzato alcun indice vettoriale.
  • PARTIALLY_USED: alcune funzioni VECTOR_SEARCH nella query utilizzavano indici vettoriali e altre no.
  • FULLY_USED: ogni funzione VECTOR_SEARCH nella query ha utilizzato un indice vettoriale.

Quando il valore della modalità di utilizzo dell'indice è UNUSED o PARTIALLY_USED, i motivi del mancato utilizzo dell'indice indicano perché gli indici vettoriali non sono stati utilizzati nella query.

Ad esempio, i seguenti risultati restituiti da bq show --format=prettyjson -j my_job_id mostrano che l'indice non è stato utilizzato perché l'opzione use_brute_force è stata specificata nella funzione 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"
}

Opzioni di gestione degli indici

Per creare indici e farli gestire da BigQuery, hai due opzioni:

  • Utilizza il pool di slot condiviso predefinito: quando i dati che prevedi di indicizzare sono inferiori al limite per organizzazione, puoi utilizzare il pool di slot condiviso gratuito per la gestione degli indici.
  • Utilizza la tua prenotazione: Per ottenere un avanzamento dell'indicizzazione più prevedibile e coerente nei carichi di lavoro di produzione più grandi, puoi utilizzare le tue prenotazioni per la gestione dell'indice.

Utilizzare slot condivisi

Se non hai configurato il tuo progetto per utilizzare una prenotazione dedicata per l'indicizzazione, la gestione dell'indice viene gestita nel pool di slot condiviso gratuito, soggetto ai seguenti vincoli.

Se aggiungi dati a una tabella che causa il superamento del limite della tua organizzazione per le tabelle indicizzate, BigQuery mette in pausa la gestione degli indici per tutte le tabelle indicizzate. In questo caso, il campo index_status nella visualizzazione INFORMATION_SCHEMA.VECTOR_INDEXES mostra PENDING DISABLEMENT e l'indice viene messo in coda per l'eliminazione. Mentre l'indice è in attesa di disattivazione, viene ancora utilizzato nelle query e ti viene addebitato lo spazio di archiviazione dell'indice. Dopo l'eliminazione di un indice, il campo index_status mostra l'indice come TEMPORARILY DISABLED. In questo stato, le query non utilizzano l'indice e non ti viene addebitato alcun costo per l'archiviazione dell'indice. In questo caso, il codice IndexUnusedReason è BASE_TABLE_TOO_LARGE.

Se elimini i dati dalla tabella e le dimensioni totali delle tabelle indicizzate scendono al di sotto del limite per organizzazione, la gestione degli indici viene ripresa per tutte le tabelle indicizzate. Il campo index_status nella vista INFORMATION_SCHEMA.VECTOR_INDEXES è ACTIVE, le query possono utilizzare l'indice e ti viene addebitato lo spazio di archiviazione dell'indice.

Puoi utilizzare la visualizzazione INFORMATION_SCHEMA.SEARCH_INDEXES_BY_ORGANIZATION per comprendere il tuo consumo attuale rispetto al limite per organizzazione in una determinata regione, suddiviso per progetti e tabelle.

BigQuery non offre garanzie sulla capacità disponibile del pool condiviso o sulla velocità effettiva di indicizzazione visualizzata. Per le applicazioni di produzione, ti consigliamo di utilizzare slot dedicati per l'elaborazione degli indici.

Utilizzare una prenotazione personale

Anziché utilizzare il pool di slot condiviso predefinito, puoi facoltativamente designare una tua prenotazione per indicizzare le tabelle. L'utilizzo della tua prenotazione garantisce un rendimento prevedibile e coerente dei job di gestione degli indici, come la creazione, l'aggiornamento e le ottimizzazioni in background.

  • Non sono previsti limiti di dimensioni delle tabelle quando un job di indicizzazione viene eseguito nella tua prenotazione.
  • L'utilizzo della tua prenotazione ti offre flessibilità nella gestione dell'indice. Se devi creare un indice molto grande o apportare un aggiornamento importante a una tabella indicizzata, puoi aggiungere temporaneamente altri slot all'assegnazione.

Per indicizzare le tabelle in un progetto con una prenotazione designata, crea una prenotazione nella regione in cui si trovano le tabelle. Poi, assegna il progetto alla prenotazione con job_type impostato su BACKGROUND:

SQL

Utilizza l'istruzione DDL CREATE ASSIGNMENT.

  1. Nella console Google Cloud , vai alla pagina BigQuery.

    Vai a BigQuery

  2. Nell'editor di query, inserisci la seguente istruzione:

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

    Sostituisci quanto segue:

    • ADMIN_PROJECT_ID: l'ID progetto del progetto di amministrazione proprietario della risorsa di prenotazione
    • LOCATION: la posizione della prenotazione
    • RESERVATION_NAME: il nome della prenotazione
    • ASSIGNMENT_ID: l'ID dell'assegnazione

      L'ID deve essere univoco per il progetto e la località, iniziare e terminare con una lettera minuscola o un numero e contenere solo lettere minuscole, numeri e trattini.

    • PROJECT_ID: l'ID del progetto contenente le tabelle da indicizzare. Questo progetto è assegnato alla prenotazione.

  3. Fai clic su Esegui.

Per maggiori informazioni su come eseguire le query, consulta Eseguire una query interattiva.

bq

Utilizza il comando 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

Sostituisci quanto segue:

  • ADMIN_PROJECT_ID: l'ID progetto del progetto di amministrazione che possiede la risorsa di prenotazione
  • LOCATION: la posizione della prenotazione
  • RESERVATION_NAME: il nome della prenotazione
  • PROJECT_ID: l'ID del progetto da assegnare a questa prenotazione

Visualizzare i job di indicizzazione

Ogni volta che viene creato o aggiornato un indice in una singola tabella, viene creato un nuovo job di indicizzazione. Per visualizzare informazioni sul job, esegui una query sulle viste INFORMATION_SCHEMA.JOBS*. Puoi filtrare i job di indicizzazione impostando job_type IS NULL AND SEARCH(job_id, '`search_index`') nella clausola WHERE della query. L'esempio seguente elenca i cinque job di indicizzazione più recenti nel progetto 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;

Scegli le dimensioni della prenotazione

Per scegliere il numero corretto di slot per la prenotazione, devi considerare quando vengono eseguiti i job di gestione degli indici, quanti slot utilizzano e come appare il tuo utilizzo nel tempo. BigQuery attiva un job di gestione degli indici nelle seguenti situazioni:

  • Crei un indice in una tabella.
  • I dati vengono modificati in una tabella indicizzata.
  • Lo schema di una tabella cambia e ciò influisce sulle colonne indicizzate.
  • I dati e i metadati dell'indice vengono periodicamente ottimizzati o aggiornati.

Il numero di slot necessari per un job di gestione degli indici su una tabella dipende dai seguenti fattori:

  • Le dimensioni della tabella
  • Il tasso di importazione dati nella tabella
  • Il tasso di istruzioni DML applicate alla tabella
  • Il ritardo accettabile per la creazione e la manutenzione dell'indice
  • La complessità dell'indice, in genere determinata dagli attributi dei dati, ad esempio il numero di termini duplicati
Monitorare l'utilizzo e i progressi

Il modo migliore per valutare il numero di slot necessari per eseguire in modo efficiente i job di gestione degli indici è monitorare l'utilizzo degli slot e modificare le dimensioni della prenotazione di conseguenza. La seguente query produce l'utilizzo giornaliero degli slot per i job di gestione degli indici. Solo gli ultimi 30 giorni sono inclusi nella regione 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;

Quando non ci sono slot sufficienti per eseguire i job di gestione degli indici, un indice può diventare non sincronizzato con la tabella e i job di indicizzazione potrebbero non riuscire. In questo caso, BigQuery ricompila l'indice da zero. Per evitare di avere un indice non sincronizzato, assicurati di disporre di slot sufficienti per supportare gli aggiornamenti dell'indice dall'importazione e dall'ottimizzazione dei dati. Per ulteriori informazioni sul monitoraggio dell'utilizzo degli slot, consulta grafici delle risorse amministrative.

Eliminare un indice vettoriale

Quando non hai più bisogno di un indice vettoriale o vuoi modificare la colonna indicizzata in una tabella, puoi eliminare l'indice nella tabella utilizzando l'istruzione DDL DROP VECTOR INDEX.

Ad esempio:

DROP VECTOR INDEX my_index ON my_dataset.indexed_table;

Se una tabella indicizzata viene eliminata, il relativo indice viene eliminato automaticamente.

Passaggi successivi