Gestisci indici di ricerca

Un indice di ricerca è una struttura di dati progettata per consentire ricerche in modo molto efficiente con la funzione SEARCH. Proprio come l'indice che si trova sul retro di un libro, un indice di ricerca per una colonna di dati stringa funziona come una tabella ausiliaria che ha una colonna per le parole univoche e un'altra per la posizione nei dati.

Crea un indice di ricerca

Per creare un indice di ricerca, utilizza l'istruzione DDL CREATE SEARCH INDEX. Se non viene specificato un tipo di dati per una colonna, puoi creare un indice di ricerca su questi tipi di colonna:

  • STRING
  • ARRAY<STRING>
  • STRUCT contenente almeno un campo nidificato di tipo STRING o ARRAY<STRING>
  • JSON

Quando crei un indice di ricerca, puoi specificare il tipo di analizzatore di testo da utilizzare. Lo strumento di analisi del testo controlla il modo in cui i dati vengono tokenizzati per l'indicizzazione e la ricerca. Il valore predefinito è LOG_ANALYZER. Questo analizzatore funziona bene per i log generati dalle macchine e ha regole speciali per i token che si trovano comunemente nei dati di osservabilità, come indirizzi IP o email. Utilizza NO_OP_ANALYZER quando disponi di dati pre-elaborati che vuoi far corrispondere esattamente. PATTERN_ANALYZER estrae i token dal testo utilizzando un'espressione regolare.

Crea un indice di ricerca con lo strumento di analisi di testo predefinito

Nell'esempio seguente, nelle colonne a e c di simple_table viene creato un indice di ricerca che utilizza lo strumento di analisi di testo LOG_ANALYZER per impostazione predefinita:

CREATE TABLE dataset.simple_table(a STRING, b INT64, c JSON);

CREATE SEARCH INDEX my_index
ON dataset.simple_table(a, c);

Crea un indice di ricerca su tutte le colonne con lo strumento di analisi NO_OP_ANALYZER

Quando crei un indice di ricerca su ALL COLUMNS, vengono indicizzati tutti i dati di STRING o JSON nella tabella. Se la tabella non contiene dati di questo tipo, ad esempio se tutte le colonne contengono numeri interi, la creazione dell'indice non riesce. Se specifichi una colonna STRUCT da indicizzare, vengono indicizzati tutti i campi secondari nidificati.

Nell'esempio seguente, viene creato un indice di ricerca su a, c.e e c.f.g, che utilizza lo strumento di analisi NO_OP_ANALYZER di testo:

CREATE TABLE dataset.my_table(
  a STRING,
  b INT64,
  c STRUCT <d INT64,
            e ARRAY<STRING>,
            f STRUCT<g STRING, h INT64>>) AS
SELECT 'hello' AS a, 10 AS b, (20, ['x', 'y'], ('z', 30)) AS c;

CREATE SEARCH INDEX my_index
ON dataset.my_table(ALL COLUMNS)
OPTIONS (analyzer = 'NO_OP_ANALYZER');

Poiché l'indice di ricerca è stato creato il giorno ALL COLUMNS, tutte le colonne aggiunte alla tabella vengono indicizzate automaticamente se contengono dati di STRING.

Crea un indice di ricerca e specifica le colonne e i tipi di dati

Quando crei un indice di ricerca, puoi specificare i tipi di dati da utilizzare. I tipi di dati controllano i tipi di colonne e sottocampi delle colonne JSON e STRUCT per l'indicizzazione. Il tipo di dati predefinito per l'indicizzazione è STRING. Per creare un indice di ricerca con più tipi di dati (ad esempio tipi numerici), utilizza l'istruzione CREATE SEARCH INDEX con l'opzione data_types inclusa.

Nell'esempio seguente, viene creato un indice di ricerca nelle colonne a, b, c e d di una tabella denominata simple_table. I tipi di dati delle colonne supportati sono STRING, INT64 e TIMESTAMP.

CREATE TABLE dataset.simple_table(a STRING, b INT64, c JSON, d TIMESTAMP);

CREATE SEARCH INDEX my_index
ON dataset.simple_table(a, b, c, d)
OPTIONS ( data_types = ['STRING', 'INT64', 'TIMESTAMP']);

Crea un indice di ricerca su tutte le colonne e specifica i tipi di dati

Quando crei un indice di ricerca su ALL COLUMNS con l'opzione data_types specificata, viene indicizzata ogni colonna che corrisponde a uno dei tipi di dati specificati. Per le colonne JSON e STRUCT, viene indicizzato qualsiasi campo secondario nidificato che corrisponde a uno dei tipi di dati specificati.

Nell'esempio seguente, viene creato un indice di ricerca su ALL COLUMNS con i tipi di dati specificati. Vengono indicizzate le colonne a, b, c, d.e, d.f, d.g.h, d.g.i di una tabella denominata my_table:

CREATE TABLE dataset.my_table(
  a STRING,
  b INT64,
  c TIMESTAMP,
  d STRUCT <e INT64,
            f ARRAY<STRING>,
            g STRUCT<h STRING, i INT64>>)
AS (
  SELECT
    'hello' AS a,
    10 AS b,
    TIMESTAMP('2008-12-25 15:30:00 UTC') AS c,
    (20, ['x', 'y'], ('z', 30)) AS d;
)

CREATE SEARCH INDEX my_index
ON dataset.my_table(ALL COLUMNS)
OPTIONS ( data_types = ['STRING', 'INT64', 'TIMESTAMP']);

Poiché l'indice di ricerca è stato creato il giorno ALL COLUMNS, tutte le colonne aggiunte alla tabella vengono indicizzate automaticamente se corrispondono a uno qualsiasi dei tipi di dati specificati.

Informazioni sull'aggiornamento dell'indice

Gli indici di ricerca sono completamente gestiti da BigQuery e aggiornati automaticamente quando la tabella cambia. Le seguenti modifiche allo schema della tabella possono attivare un aggiornamento completo:

  • Una nuova colonna indicizzabile viene aggiunta a una tabella con un indice di ricerca su ALL COLUMNS.
  • Una colonna indicizzata viene aggiornata a causa di una modifica allo schema della tabella.

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

Gli indici di ricerca sono progettati per tabelle di grandi dimensioni. Se crei un indice di ricerca in una tabella di dimensioni inferiori a 10 GB, l'indice non viene compilato. Allo stesso modo, se elimini dati da una tabella indicizzata e le sue dimensioni scendono al di sotto di 10 GB, l'indice viene temporaneamente disattivato. In questo caso, le query di ricerca non utilizzano l'indice e il codice IndexUnusedReason è BASE_TABLE_TOO_SMALL. Questo accade indipendentemente dal fatto che utilizzi o meno la tua prenotazione per i job di gestione degli indici. Quando le dimensioni di una tabella indicizzata superano i 10 GB, il relativo indice viene compilato automaticamente. L'archiviazione non viene addebitata finché l'indice di ricerca non è compilato e attivo. Le query che utilizzano la funzione SEARCH restituiscono sempre risultati corretti anche se alcuni dati non sono ancora stati indicizzati.

Ottieni informazioni sugli indici di ricerca

Puoi verificare l'esistenza e l'idoneità di un indice di ricerca eseguendo una query su INFORMATION_SCHEMA. Due visualizzazioni contengono metadati sugli indici di ricerca. La vista INFORMATION_SCHEMA.SEARCH_INDEXES contiene informazioni su ogni indice di ricerca creato in un set di dati. La vista INFORMATION_SCHEMA.SEARCH_INDEX_COLUMNS contiene informazioni sulle colonne di ogni tabella del set di dati indicizzate.

L'esempio seguente mostra tutti gli indici di ricerca attivi nelle tabelle nel set di dati my_dataset, situato nel progetto my_project. Sono inclusi i nomi, le istruzioni DDL utilizzate per crearle, la percentuale di copertura e lo strumento di analisi del testo. Se una tabella di base indicizzata è inferiore a 10 GB, il suo indice non viene compilato e in questo caso coverage_percentage è 0.

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

I risultati dovrebbero essere simili ai seguenti:

+-------------+-------------+--------------------------------------------------------------------------------------+---------------------+----------------+
| table_name  | index_name  | ddl                                                                                  | coverage_percentage | analyzer       |
+-------------+-------------+--------------------------------------------------------------------------------------+---------------------+----------------+
| small_table | names_index | CREATE SEARCH INDEX `names_index` ON `my_project.my_dataset.small_table`(names)      | 0                   | NO_OP_ANALYZER |
| large_table | logs_index  | CREATE SEARCH INDEX `logs_index` ON `my_project.my_dataset.large_table`(ALL COLUMNS) | 100                 | LOG_ANALYZER   |
+-------------+-------------+--------------------------------------------------------------------------------------+---------------------+----------------+

L'esempio seguente crea un indice di ricerca su tutte le colonne di my_table.

CREATE TABLE dataset.my_table(
  a STRING,
  b INT64,
  c STRUCT <d INT64,
            e ARRAY<STRING>,
            f STRUCT<g STRING, h INT64>>) AS
SELECT 'hello' AS a, 10 AS b, (20, ['x', 'y'], ('z', 30)) AS c;

CREATE SEARCH INDEX my_index
ON dataset.my_table(ALL COLUMNS);

La seguente query estrae informazioni sui campi indicizzati. index_field_path indica quale campo di una colonna è indicizzato. Questo valore differisce dal index_column_name solo nel caso di un STRUCT, in cui viene specificato il percorso completo del campo indicizzato. In questo esempio, la colonna c contiene un campo ARRAY<STRING> e e un altro STRUCT denominato f che contiene un campo STRING g, ognuno dei quali è indicizzato.

SELECT table_name, index_name, index_column_name, index_field_path
FROM my_project.dataset.INFORMATION_SCHEMA.SEARCH_INDEX_COLUMNS

Il risultato è simile al seguente:

+------------+------------+-------------------+------------------+
| table_name | index_name | index_column_name | index_field_path |
+------------+------------+-------------------+------------------+
| my_table   | my_index   | a                 | a                |
| my_table   | my_index   | c                 | c.e              |
| my_table   | my_index   | c                 | c.f.g            |
+------------+------------+-------------------+------------------+

La seguente query unisce la vista INFORMATION_SCHEMA.SEARCH_INDEX_COUMNS alle viste INFORMATION_SCHEMA.SEARCH_INDEXES e INFORMATION_SCHEMA.COLUMNS per includere lo stato dell'indice di ricerca e il tipo di dati di ogni colonna:

SELECT
  index_columns_view.index_catalog AS project_name,
  index_columns_view.index_SCHEMA AS dataset_name,
  indexes_view.TABLE_NAME AS table_name,
  indexes_view.INDEX_NAME AS index_name,
  indexes_view.INDEX_STATUS AS status,
  index_columns_view.INDEX_COLUMN_NAME AS column_name,
  index_columns_view.INDEX_FIELD_PATH AS field_path,
  columns_view.DATA_TYPE AS data_type
FROM
  mydataset.INFORMATION_SCHEMA.SEARCH_INDEXES indexes_view
INNER JOIN
  mydataset.INFORMATION_SCHEMA.SEARCH_INDEX_COLUMNS index_columns_view
  ON
    indexes_view.TABLE_NAME = index_columns_view.TABLE_NAME
    AND indexes_view.INDEX_NAME = index_columns_view.INDEX_NAME
LEFT OUTER JOIN
  mydataset.INFORMATION_SCHEMA.COLUMNS columns_view
  ON
    indexes_view.INDEX_CATALOG = columns_view.TABLE_CATALOG
    AND indexes_view.INDEX_SCHEMA = columns_view.TABLE_SCHEMA
    AND index_columns_view.TABLE_NAME = columns_view.TABLE_NAME
    AND index_columns_view.INDEX_COLUMN_NAME = columns_view.COLUMN_NAME
ORDER BY
  project_name,
  dataset_name,
  table_name,
  column_name;

Il risultato è simile al seguente:

+------------+------------+----------+------------+--------+-------------+------------+---------------------------------------------------------------+
| project    | dataset    | table    | index_name | status | column_name | field_path | data_type                                                     |
+------------+------------+----------+------------+--------+-------------+------------+---------------------------------------------------------------+
| my_project | my_dataset | my_table | my_index   | ACTIVE | a           | a          | STRING                                                        |
| my_project | my_dataset | my_table | my_index   | ACTIVE | c           | c.e        | STRUCT<d INT64, e ARRAY<STRING>, f STRUCT<g STRING, h INT64>> |
| my_project | my_dataset | my_table | my_index   | ACTIVE | c           | c.f.g      | STRUCT<d INT64, e ARRAY<STRING>, f STRUCT<g STRING, h INT64>> |
+------------+------------+----------+------------+--------+-------------+------------+---------------------------------------------------------------+

Opzioni di gestione degli indici

Per creare indici e fare in modo che BigQuery li mantenga, hai due opzioni:

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

Utilizza slot condivisi

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

Se aggiungi dati a una tabella per far sì che la dimensione totale delle tabelle indicizzate superi il limite della tua organizzazione, BigQuery mette in pausa la gestione dell'indice per tutte le tabelle indicizzate. In questo caso, il campo index_status nella vista INFORMATION_SCHEMA.SEARCH_INDEXES mostra PENDING DISABLEMENT e l'indice viene messo in coda per l'eliminazione. Mentre l'indice è in attesa di disabilitazione, viene comunque utilizzato nelle query e ti viene addebitato l'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 l'archiviazione dell'indice. In questo caso, il codice IndexUnusedReason è BASE_TABLE_TOO_LARGE.

Se elimini i dati dalla tabella e la dimensione totale delle tabelle indicizzate scende al di sotto del limite per organizzazione, la gestione dell'indice viene ripresa per tutte le tabelle indicizzate. Il campo index_status nella vista INFORMATION_SCHEMA.SEARCH_INDEXES è ACTIVE, le query possono utilizzare l'indice e ti viene addebitato l'archiviazione dell'indice.

BigQuery non garantisce la capacità disponibile del pool condiviso o la velocità effettiva di indicizzazione visualizzata. Per le applicazioni di produzione è consigliabile usare slot dedicati per l'elaborazione dell'indice.

Utilizza la tua prenotazione

Anziché utilizzare il pool di slot condiviso predefinito, puoi facoltativamente specificare la tua prenotazione per indicizzare le tabelle. L'utilizzo della tua prenotazione garantisce prestazioni prevedibili e coerenti dei job di gestione degli indici, come creazione, aggiornamento e ottimizzazioni in background.

  • Non ci sono limiti di dimensioni delle tabelle quando un job di indicizzazione viene eseguito nella prenotazione.
  • L'utilizzo della tua prenotazione ti offre flessibilità nella gestione dell'indice. Se devi creare un indice molto grande o effettuare un aggiornamento importante a una tabella indicizzata, puoi aggiungere temporaneamente più 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. Quindi, assegna il progetto alla prenotazione con il 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 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 del progetto di amministrazione proprietario della risorsa di prenotazione
    • LOCATION: la località della prenotazione
    • RESERVATION_NAME: il nome della prenotazione
    • ASSIGNMENT_ID: l'ID del compito

      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 ulteriori informazioni su come eseguire le query, vedi Eseguire una query interattiva.

bq

Usa 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 del progetto di amministrazione proprietario della risorsa di prenotazione
  • LOCATION: la località 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 un indice viene creato o aggiornato in una singola tabella, viene creato un nuovo job di indicizzazione. Per visualizzare le 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 giusto di slot per la prenotazione, devi considerare quando vengono eseguiti i job di gestione dell'indice, quanti slot utilizzano e quale sarà l'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 dell'indice e i metadati vengono ottimizzati o aggiornati periodicamente.

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

  • Le dimensioni della tabella
  • La velocità di importazione dati nella tabella
  • La frequenza delle istruzioni DML applicate alla tabella
  • Il ritardo accettabile per la creazione e la gestione dell'indice
  • La complessità dell'indice, in genere determinata dagli attributi dei dati, come il numero di termini duplicati
Stima iniziale

Le seguenti stime possono aiutarti ad approssimativamente quanti slot sono necessari per la prenotazione. A causa della natura altamente variabile dei carichi di lavoro di indicizzazione, devi rivalutare i requisiti dopo aver iniziato a indicizzare i dati.

  • Dati esistenti: con una prenotazione di 1000 slot, una tabella esistente in BigQuery può essere indicizzata a una velocità media massima di 4 GiB al secondo, ovvero circa 336 TiB al giorno.
  • Dati appena importati: l'indicizzazione in genere comporta un consumo maggiore di risorse sui dati appena importati, poiché la tabella e il relativo indice vengono sottoposti a diversi cicli di ottimizzazioni trasformative. In media, l'indicizzazione dei dati appena importati consuma il triplo delle risorse rispetto all'indicizzazione iniziale del backfill degli stessi dati.
  • Dati modificati raramente: le tabelle indicizzate con una modifica minima o nulla dei dati richiedono molte meno risorse per una continua manutenzione dell'indice. Un punto di partenza consigliato è mantenere 1/5 degli slot necessari per l'indicizzazione di backfill iniziale degli stessi dati e non meno di 250 slot.
  • L'avanzamento dell'indicizzazione scala in modo più o meno lineare con la dimensione della prenotazione. Tuttavia, sconsigliamo di utilizzare prenotazioni di dimensioni inferiori a 250 slot per l'indicizzazione, perché ciò potrebbe causare inefficienze che possono rallentare l'avanzamento dell'indicizzazione.
  • Queste stime possono variare in base alle funzionalità, alle ottimizzazioni e all'utilizzo effettivo.
  • Se le dimensioni totali della tabella della tua organizzazione superano il limite di indicizzazione della tua regione, devi mantenere una prenotazione diversa da zero assegnata per l'indicizzazione. In caso contrario, l'indicizzazione potrebbe tornare al livello predefinito, con conseguente eliminazione involontaria di tutti gli indici.
Monitora 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 regolare le dimensioni della prenotazione di conseguenza. La seguente query restituisce l'utilizzo giornaliero degli slot per i job di gestione degli indici. Nella regione us-west1 sono inclusi solo gli ultimi 30 giorni:

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;

Se gli slot non sono sufficienti per eseguire i job di gestione dell'indice, un indice può non essere sincronizzato con la sua tabella e i job di indicizzazione potrebbero non riuscire. In questo caso, BigQuery ricrea l'indice da zero. Per evitare di avere un indice non sincronizzato, assicurati di avere 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 i grafici delle risorse di amministrazione.

Best practice

  • Gli indici di ricerca sono progettati per tabelle di grandi dimensioni. Il miglioramento delle prestazioni derivante da un aumento dell'indice di ricerca con le dimensioni della tabella.
  • Non indicizzare colonne che contengono solo un numero molto ridotto di valori univoci.
  • Non indicizzare le colonne su cui non intendi mai chiamare la funzione SEARCH.
  • Fai attenzione quando crei un indice di ricerca su ALL COLUMNS. Ogni volta che aggiungi una colonna contenente dati STRING o JSON, questa viene indicizzata.
  • Dovresti utilizzare la tua prenotazione per la gestione degli indici nelle applicazioni di produzione. Se scegli di utilizzare il pool di slot condiviso predefinito per i job di gestione dell'indice, si applicano i limiti di dimensioni per organizzazione.

Eliminare un indice di ricerca

Quando non hai più bisogno di un indice di ricerca o vuoi modificare le colonne indicizzate in una tabella, puoi eliminare l'indice attualmente presente nella tabella. A questo scopo, utilizza l'istruzione DDL DROP SEARCH INDEX.

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

Esempio:

DROP SEARCH INDEX my_index ON dataset.simple_table;

Passaggi successivi

  • Per una panoramica dei casi d'uso, dei prezzi, delle autorizzazioni richieste e delle limitazioni relative all'indice di ricerca, consulta Introduzione alla ricerca in BigQuery.
  • Per informazioni su come eseguire una ricerca efficiente nelle colonne indicizzate, consulta la pagina relativa alla ricerca con un indice.