Ottimizzare il calcolo delle query
Questo documento fornisce le best practice per ottimizzare le prestazioni delle query.
Al termine della query, puoi
visualizzare il piano di query
nella console Google Cloud. Puoi anche richiedere i dettagli di esecuzione utilizzando le visualizzazioni INFORMATION_SCHEMA.JOBS*
o il metodo dell'API REST jobs.get
.
Il piano di query include dettagli sulle fasi e sui passaggi della query. Questi dettagli possono aiutarti a identificare i modi per migliorare le prestazioni delle query. Ad esempio, se noti una fase che scrive molto più output rispetto ad altre fasi, potrebbe significare che devi applicare un filtro all'inizio della query.
Per scoprire di più sul piano di query e vedere esempi di come le informazioni sul piano di query possono aiutarti a migliorare le prestazioni delle query, consulta Ottenere informazioni sulle prestazioni delle query. Dopo aver esaminato gli approfondimenti sul rendimento delle query, puoi ottimizzare ulteriormente la query svolgendo le seguenti attività:
- Riduci i dati da elaborare
- Ottimizzare le operazioni di query
- Riduci l'output della query
- Utilizzare una prenotazione di BigQuery BI Engine
- Evita gli anti-pattern SQL
- Specificare i vincoli nello schema della tabella
Riduci i dati elaborati
Puoi ridurre i dati che devono essere elaborati utilizzando le opzioni descritte nelle sezioni seguenti.
Evita SELECT *
Best practice:controlla la proiezione eseguendo query solo sulle colonne di cui hai bisogno.
La proiezione si riferisce al numero di colonne lette dalla query. La proiezione di colonne in eccesso comporta un'I/O aggiuntiva (sprecata) e la materializzazione (scrittura dei risultati).
- Utilizza le opzioni di anteprima dei dati. Se stai conducendo esperimenti o esplorando i dati, utilizza una delle
opzioni di anteprima dei dati anziché
SELECT *
. - Esegui query su colonne specifiche. L'applicazione di una clausola
LIMIT
a una querySELECT *
non influisce sulla quantità di dati letti. Ti viene addebitato il costo per la lettura di tutti i byte dell'intera tabella e la query viene conteggiata ai fini della quota del livello gratuito. Esegui invece query solo sulle colonne di cui hai bisogno. Ad esempio, utilizzaSELECT * EXCEPT
per escludere una o più colonne dai risultati. - Utilizza tabelle partizionate. Se hai bisogno di eseguire query su ogni colonna di una tabella, ma solo su un
sottoinsieme di dati, valuta la possibilità di:
- Materializzare i risultati in una tabella di destinazione ed eseguire query su questa tabella.
- Eseguire il partizionamento delle tabelle
e eseguire query sulla partizione pertinente.
Ad esempio, utilizza
WHERE _PARTITIONDATE="2017-01-01"
per eseguire query solo sulla partizione del 1° gennaio 2017.
Utilizza
SELECT * EXCEPT
. L'esecuzione di query su un sottoinsieme di dati o l'utilizzo diSELECT * EXCEPT
puoi ridurre notevolmente la quantità di dati letti da una query. Oltre al risparmio sui costi, le prestazioni vengono migliorate riducendo la quantità di I/O dei dati e la quantità di materializzazione necessaria per i risultati della query.SELECT * EXCEPT (col1, col2, col5) FROM mydataset.newtable
Evita tabelle con troppi caratteri jolly
Best practice: quando esegui query sulle tabelle con funzione carattere jolly, devi utilizzare il prefisso più granulare.
Utilizza i caratteri jolly per eseguire query su più tabelle utilizzando istruzioni SQL concise. Le tabelle con caratteri jolly sono un'unione di tabelle che corrispondono all'espressione con caratteri jolly. Le tabelle con caratteri jolly sono utili se il set di dati contiene le seguenti risorse:
- Più tabelle con nomi simili e schemi compatibili
- Tabelle con sharding
Quando esegui una query su una tabella con funzione carattere jolly, specifica un carattere jolly (*
) dopo il prefisso della tabella comune. Ad esempio, FROM
esegue query su tutte le tabelle degli anni '40.bigquery-public-data.noaa_gsod.gsod194*
I prefissi più granulari hanno un rendimento migliore rispetto ai prefissi più brevi. Ad esempio,
FROM
ha un rendimento migliore di
bigquery-public-data.noaa_gsod.gsod194*
FROM
perché meno tabelle corrispondono
al carattere jolly.bigquery-public-data.noaa_gsod.*
Evita le tabelle suddivise in parti per data
Best practice: non utilizzare tabelle suddivise per data (chiamate anche tabelle con nome della data) al posto di tabelle partizionate in base al tempo.
Le tabelle partizionate hanno un rendimento migliore rispetto alle tabelle con nomi di date. Quando crei tabelle suddivise per data, BigQuery deve mantenere una copia dello schema e dei metadati per ogni tabella con nome della data. Inoltre, quando vengono utilizzate tabelle con nomi di date, BigQuery potrebbe dover verificare le autorizzazioni per ogni tabella sottoposta a query. Questa pratica contribuisce anche all'overhead delle query e influisce sulle relative prestazioni.
Evita di eseguire lo sharding eccessivo delle tabelle
Best practice:evita di creare troppi shard di tabella. Se esegui lo sharding delle tabelle in base alla data, utilizza invece le tabelle partizionate in base al tempo.
Per sharding delle tabelle si intende la suddivisione di set di dati di grandi dimensioni in tabelle separate e l'aggiunta di un suffisso a ogni nome di tabella. Se esegui lo sharding delle tabelle per data, utilizza invece le tabelle partizionate in base al tempo.
A causa del basso costo dello spazio di archiviazione BigQuery, non è necessario ottimizzare le tabelle in base al costo come faresti in un sistema di database relazionale. La creazione di un numero elevato di frammenti di tabella ha un impatto sulle prestazioni che supera qualsiasi vantaggio in termini di costi.
Le tabelle suddivise in parti richiedono a BigQuery di gestire lo schema, i metadati e le autorizzazioni per ogni shard. A causa del sovraccarico aggiuntivo necessario per mantenere le informazioni su ogni shard, le tabelle con un numero eccessivo di shard possono influire sulle prestazioni delle query.
La quantità e l'origine dei dati letti da una query possono influire sulle prestazioni e sui costi della query.
Elimina le query partizionate
Best practice:quando esegui una query su una tabella partizionata, per filtrare con le partizioni sulle tabelle partizionate, utilizza le seguenti colonne:
- Per le tabelle partizionate per data di importazione, utilizza la pseudocolonna
_PARTITIONTIME
- Per le tabelle partizionate, ad esempio quelle basate su colonne di unità di tempo e intervallo di interi, utilizza la colonna di partizionamento.
Per le tabelle partizionate in base a unità di tempo, filtrare i dati con _PARTITIONTIME
o con la colonna di partizionamento ti consente di specificare una data o un intervallo di date. Ad esempio,
la seguente clausola WHERE
utilizza la pseudocolonna _PARTITIONTIME
per specificare
le partizioni tra il 1° gennaio 2016 e il 31 gennaio 2016:
WHERE _PARTITIONTIME
BETWEEN TIMESTAMP("20160101")
AND TIMESTAMP("20160131")
La query elabora i dati solo nelle partizioni indicate dall'intervallo di date. Filtrare le partizioni migliora le prestazioni delle query e riduce i costi.
Riduci i dati prima di utilizzare un JOIN
Best practice:riduci la quantità di dati elaborati prima di una clausola JOIN
eseguendo aggregazioni.
L'utilizzo di una clausola GROUP BY
con funzioni aggregate richiede un'elaborazione intensa, perché questi tipi di query utilizzano il riordinamento.
Poiché queste query richiedono un'elaborazione intensa, devi utilizzare una clausola GROUP BY
solo quando necessario.
Per le query con GROUP BY
e JOIN
, esegui l'aggregazione all'inizio della query per ridurre la quantità di dati elaborati.
Ad esempio, la seguente query esegue un JOIN
su due tabelle di grandi dimensioni senza alcun filtro preliminare:
WITH users_posts AS ( SELECT * FROM `bigquery-public-data`.stackoverflow.comments AS c JOIN `bigquery-public-data`.stackoverflow.users AS u ON c.user_id = u.id ) SELECT user_id, ANY_VALUE(display_name) AS display_name, ANY_VALUE(reputation) AS reputation, COUNT(text) AS comments_count FROM users_posts GROUP BY user_id ORDER BY comments_count DESC LIMIT 20;
Questa query preaggrega i conteggi dei commenti, il che riduce la quantità di dati
letti per JOIN
:
WITH comments AS ( SELECT user_id, COUNT(text) AS comments_count FROM `bigquery-public-data`.stackoverflow.comments WHERE user_id IS NOT NULL GROUP BY user_id ORDER BY comments_count DESC LIMIT 20 ) SELECT user_id, display_name, reputation, comments_count FROM comments JOIN `bigquery-public-data`.stackoverflow.users AS u ON user_id = u.id ORDER BY comments_count DESC;
Utilizzare la clausola WHERE
Best practice:utilizza una
clausola WHERE
per limitare la quantità di dati restituiti da una query. Se possibile, utilizza le colonne BOOL
, INT
, FLOAT
o DATE
nella clausola WHERE
.
Le operazioni sulle colonne BOOL
, INT
, FLOAT
e DATE
sono in genere più rapide
delle operazioni sulle colonne STRING
o BYTE
. Se possibile, utilizza una colonna che utilizzi uno di questi tipi di dati nella clausola WHERE
per ridurre la quantità di dati restituiti dalla query.
Ottimizzare le operazioni di query
Puoi ottimizzare le operazioni di query utilizzando le opzioni descritte nelle sezioni seguenti.
Evita di trasformare ripetutamente i dati
Best practice:se utilizzi SQL per eseguire operazioni ETL, evita situazioni in cui trasformi ripetutamente gli stessi dati.
Ad esempio, se utilizzi SQL per tagliare le stringhe o estrarre i dati utilizzando espressioni regolari, è più efficace materializzare i risultati trasformati in una tabella di destinazione. Funzioni come le espressioni regolari richiedono calcoli aggiuntivi. Eseguire query sulla tabella di destinazione senza l'overhead aggiuntivo della trasformazione è molto più efficiente.
Evitare più valutazioni delle stesse CTE
Best practice: utilizza un linguaggio procedurale, variabili, tabelle temporanee e tabelle con scadenza automatica per mantenere i calcoli e utilizzarli in seguito nella query.
Quando la query contiene espressioni tabella comuni (CTE) che vengono utilizzate in più punti della query, potrebbero essere valutate ogni volta che viene fatto riferimento a esse. L'ottimizzatore delle query tenta di rilevare le parti della query che potrebbero essere eseguite una sola volta, ma ciò potrebbe non essere sempre possibile. Di conseguenza, l'utilizzo di un CTE potrebbe non contribuire a ridurre la complessità delle query interne e il consumo di risorse.
Puoi memorizzare il risultato di una CTE in una variabile scalare o in una tabella temporanea, a seconda dei dati restituiti dalla CTE.
Evita unioni e sottoquery ripetute
Best practice:evita di unire ripetutamente le stesse tabelle e di utilizzare le stesse subquery.
Invece di unire ripetutamente i dati, potrebbe essere più efficace utilizzare dati ripetuti nidificati per rappresentare le relazioni. I dati ripetuti nidificati ti consentono di risparmiare l'impatto sulle prestazioni della larghezza di banda di comunicazione richiesta da un join. Inoltre, ti consente di risparmiare sui costi di I/O sostenuti leggendo e scrivendo ripetutamente gli stessi dati. Per ulteriori informazioni, consulta la sezione Utilizzare campi nidificati e ripetuti.
Analogamente, la ripetizione delle stesse sottoquery influisce sul rendimento tramite l'elaborazione ripetitiva delle query. Se utilizzi le stesse sottoquery in più query, considera la possibilità di materializzare i risultati delle sottoquery in una tabella. Poi utilizza i dati materializzati nelle query.
La materializzazione dei risultati delle sottoquery migliora le prestazioni e riduce la quantità complessiva di dati letti e scritti da BigQuery. Il piccolo costo di archiviazione dei dati materializzati supera l'impatto sulle prestazioni dell'elaborazione ripetuta di query e I/O.
Ottimizza i pattern di join
Best practice:per le query che uniscono i dati di più tabelle, ottimizza i pattern di join iniziando dalla tabella più grande.
Quando crei una query utilizzando una clausola JOIN
, tieni presente l'ordine in cui fondi i dati. L'ottimizzatore delle query GoogleSQL determina quale tabella deve trovarsi su quale lato dell'unione. Come best practice, posiziona prima la tabella con il maggior numero di righe, poi la tabella con il minor numero di righe e infine le tabelle rimanenti in ordine decrescente di dimensioni.
Quando hai una tabella grande sul lato sinistro del JOIN
e una piccola sul lato destro del JOIN
, viene creato un join di trasmissione. Un join di tipo broadcast
invia tutti i dati della tabella più piccola a ogni slot che elabora la tabella più grande. Ti consigliamo di eseguire prima l'unione alla trasmissione.
Per visualizzare le dimensioni delle tabelle in JOIN
, consulta
Ottenere informazioni sulle tabelle.
Ottimizza la clausola ORDER BY
Best practice:quando utilizzi la clausola ORDER BY
, assicurati di seguire le best practice:
Utilizza
ORDER BY
nella query più esterna o all'interno delle clausole window. Sposta le operazioni complesse alla fine della query. L'inserimento di una clausolaORDER BY
al centro di una query influisce notevolmente sul rendimento, a meno che non venga utilizzata in una funzione finestra.Un'altra tecnica per ordinare la query consiste nel spostare le operazioni complesse, come le espressioni regolari e le funzioni matematiche, alla fine della query. Questa tecnica riduce i dati da elaborare prima di eseguire le operazioni complesse.
Utilizza una clausola
LIMIT
. Se ordini un numero molto elevato di valori, ma non è necessario che vengano restituiti tutti, utilizza una clausolaLIMIT
. Ad esempio, la seguente query ordina un set di risultati molto grande e genera un erroreResources exceeded
. La query ordina in base alla colonnatitle
inmytable
. La colonnatitle
contiene milioni di valori.SELECT title FROM `my-project.mydataset.mytable` ORDER BY title;
Per rimuovere l'errore, utilizza una query come la seguente:
SELECT title FROM `my-project.mydataset.mytable` ORDER BY title DESC LIMIT 1000;
Utilizza una funzione finestra. Se ordini un numero molto elevato di valori, utilizza una funzione finestra e limita i dati prima di chiamarla. Ad esempio, la seguente query elenca i dieci utenti di Stack Overflow più antichi e il relativo ranking, con l'account più antico in fondo alla classifica:
SELECT id, reputation, creation_date, DENSE_RANK() OVER (ORDER BY creation_date) AS user_rank FROM bigquery-public-data.stackoverflow.users ORDER BY user_rank ASC LIMIT 10;
L'esecuzione di questa query richiede circa 15 secondi. Questa query utilizza
LIMIT
alla fine della query, ma non nella funzione finestraDENSE_RANK() OVER
. Per questo motivo, la query richiede che tutti i dati vengano ordinati su un singolo nodo worker.Per migliorare le prestazioni, dovresti invece limitare il set di dati prima di calcolare la funzione finestra:
WITH users AS ( SELECT id, reputation, creation_date, FROM bigquery-public-data.stackoverflow.users ORDER BY creation_date ASC LIMIT 10) SELECT id, reputation, creation_date, DENSE_RANK() OVER (ORDER BY creation_date) AS user_rank FROM users ORDER BY user_rank;
L'esecuzione di questa query richiede circa 2 secondi e restituisce gli stessi risultati della query precedente.
Un'avvertenza è che la funzione
DENSE_RANK()
assegna un ranking ai dati all'interno degli anni, pertanto per i dati del ranking che si estendono su più anni, queste query non forniscono risultati identici.
Suddividi le query complesse in query più piccole
Best practice: sfrutta le funzionalità di query con più istruzioni e le procedure archiviate per eseguire i calcoli progettati come una query complessa come più query più piccole e semplici.
Le query complesse, le funzioni REGEX
e le unioni o le sottoquery a più livelli possono essere lente
e richiedere molte risorse. A volte, provare a inserire tutti i calcoli in un unico statement SELECT
enorme, ad esempio per creare una vista, è un antipattern e può comportare una query lenta e che richiede molte risorse. In casi estremi, il piano di query interno diventa così complesso che BigQuery non è in grado di eseguirlo.
La suddivisione di una query complessa consente di visualizzare i risultati intermedi in variabili o tabelle temporanee. Puoi quindi utilizzare questi risultati intermedi in altre parti della query. È sempre più utile quando questi risultati sono necessari in più di un punto della query.
Spesso ti consente di esprimere meglio l'intenzione reale di parti della query con le tabelle temporanee come punti di materializzazione dei dati.
Utilizzare campi nidificati e ripetuti
Per informazioni su come denormalizzare lo spazio di archiviazione dei dati utilizzando campi nidificati e ripetuti, consulta Utilizzare campi nidificati e ripetuti.
Utilizzare i tipi di dati INT64
nelle unioni
Best practice:utilizza i tipi di dati INT64
nelle unioni anziché i tipi di dati STRING
per ridurre i costi e migliorare le prestazioni del confronto.
BigQuery non indicizza le chiavi primarie come i database tradizionali, quindi più ampia è la colonna di join, più lungo è il tempo necessario per il confronto. Pertanto,
l'utilizzo di tipi di dati INT64
nelle unioni è più economico ed efficiente rispetto all'utilizzo di tipi di dati STRING
.
Ridurre gli output delle query
Puoi ridurre le uscite delle query utilizzando le opzioni descritte nelle sezioni seguenti.
Materializzare set di risultati di grandi dimensioni
Best practice:valuta la possibilità di materializzare set di risultati di grandi dimensioni in una tabella di destinazione. La scrittura di set di risultati di grandi dimensioni ha un impatto sulle prestazioni e sui costi.
BigQuery limita i risultati memorizzati nella cache a circa 10 GB compressi. Le query che restituiscono risultati più grandi superano questo limite e spesso generano il seguente errore: Response too large
.
Questo errore si verifica spesso quando selezioni un numero elevato di campi da una tabella con una quantità considerevole di dati. I problemi di scrittura dei risultati memorizzati nella cache possono verificarsi anche nelle query in stile ETL che normalizzano i dati senza riduzione o aggregazione.
Puoi superare il limite relativo alle dimensioni dei risultati memorizzati nella cache utilizzando le seguenti opzioni:
- Utilizzare i filtri per limitare il set di risultati
- Utilizza una clausola
LIMIT
per ridurre il set di risultati, soprattutto se utilizzi una clausolaORDER BY
- Scrivere i dati di output in una tabella di destinazione
Puoi sfogliare i risultati utilizzando l'API REST BigQuery. Per ulteriori informazioni, consulta Eseguire la paginazione dei dati della tabella.
Utilizzare BI Engine
Per accelerare ulteriormente le query SQL memorizzando nella cache i dati che utilizzi più spesso, ti consigliamo di aggiungere una prenotazione di BI Engine al progetto in cui vengono calcolate le query.
BigQuery BI Engine utilizza un motore di query vettore per accelerare il rendimento delle query SELECT
.
Evitare gli anti-pattern SQL
Le seguenti best practice forniscono indicazioni su come evitare antipattern di query che influiscono sulle prestazioni in BigQuery.
Evita i self-join
Best practice:anziché utilizzare i self-join, utilizza una
funzione finestra (analitica)
o l'
operatore PIVOT
.
In genere, le unioni con se stessi vengono utilizzate per calcolare le relazioni dipendenti dalle righe. Il risultato dell'utilizzo di un join autonomo è che potenzialmente raddoppia il numero di righe di output. Questo aumento dei dati di output può causare prestazioni scadenti.
Evita le unioni crociate
Best practice:evita le unioni che generano più output rispetto agli input. Quando è richiesto un valore CROSS JOIN
, preaggrega i dati.
I join incrociati sono query in cui ogni riga della prima tabella viene unita a ogni riga della seconda tabella, con chiavi non univoche su entrambi i lati. L'output peggiore equivale al numero di righe nella tabella di sinistra moltiplicato per il numero di righe nella tabella di destra. In casi estremi, la query potrebbe non essere completata.
Se il job di query viene completato, la spiegazione del piano di query mostra le righe di output rispetto alle righe di input. Puoi confermare un prodotto cartesiano
modificando la query in modo da stampare il numero di righe su ciascun lato della clausola JOIN
, raggruppate in base alla chiave di join.
Per evitare problemi di prestazioni associati ai join che generano più output che input:
- Utilizza una clausola
GROUP BY
per preaggregare i dati. - Utilizza una funzione finestra. Le funzioni finestra sono spesso più efficienti dell'utilizzo di un join tra tabelle. Per ulteriori informazioni, consulta le funzioni finestra.
Evita le istruzioni DML che aggiornano o inseriscono singole righe
Best practice:evita le istruzioni DML che aggiornano o inseriscono singole righe. Esegui in batch gli aggiornamenti e gli inserimenti.
L'utilizzo di istruzioni DML specifiche per punto è un tentativo di trattare BigQuery come un sistema Online Transaction Processing (OLTP). BigQuery si concentra sull'elaborazione analitica online (OLAP) utilizzando le scansioni delle tabelle e non le ricerche puntuali. Se hai bisogno di un comportamento simile a OLTP (aggiornamenti o inserimenti di righe singole), valuta la possibilità di utilizzare un database progettato per supportare casi d'uso OLTP come Cloud SQL.
Le istruzioni DML di BigQuery sono destinate agli aggiornamenti collettivi. Le istruzioni DML UPDATE
e DELETE
in BigQuery sono orientate alle riscritture periodiche dei dati, non alle mutazioni di singole righe. L'istruzione DML INSERT
deve essere utilizzata con parsimonia. Gli inserimenti utilizzano le stesse
quote
di modifica dei job di caricamento. Se il tuo caso d'uso prevede inserimenti frequenti di righe singole, valuta la possibilità di trasmettere i dati in streaming.
Se l'aggregazione delle istruzioni UPDATE
genera molte tuple in query molto lunghe,
potresti avvicinarti al limite di lunghezza della query di 256 KB. Per aggirare il limite di lunghezza della query, valuta se gli aggiornamenti possono essere gestiti in base a criteri logici anziché a una serie di sostituzioni di tuple dirette.
Ad esempio, puoi caricare l'insieme di record di sostituzione in un'altra tabella,
quindi scrivere l'istruzione DML per aggiornare tutti i valori nella tabella originale se le
colonne non aggiornate corrispondono. Ad esempio, se i dati originali si trovano nella tabella t
e
gli aggiornamenti vengono pianificati nella tabella u
, la query sarà la seguente:
UPDATE dataset.t t SET my_column = u.my_column FROM dataset.u u WHERE t.my_key = u.my_key
Utilizza nomi di alias per le colonne con nomi simili
Best practice:utilizza gli alias di colonne e tabelle quando lavori con colonne con nomi simili nelle query, incluse le sottoquery.
Gli alias consentono di identificare le colonne e le tabelle a cui viene fatto riferimento oltre al riferimento iniziale della colonna. L'utilizzo di alias può aiutarti a comprendere e risolvere i problemi nella query SQL, ad esempio trovare le colonne utilizzate nelle sottoquery.
Specificare i vincoli nello schema della tabella
Quando i dati della tabella contengono vincoli, specificali nello schema della tabella. Il motore di query può ottimizzare i piani di query utilizzando i vincoli delle tabelle.
Specifica i vincoli della chiave primaria e della chiave esterna
Devi specificare i vincoli delle chiavi nello schema della tabella quando i dati della tabella soddisfano i requisiti di integrità dei dati dei vincoli di chiavi primarie o chiavi esterne. Il motore delle query può utilizzare le limitazioni delle chiavi per ottimizzare i piani di query. Puoi trovare informazioni dettagliate nel post del blog Ottimizzazioni dell'unione con le chiavi principali e le chiavi esterne di BigQuery.
BigQuery non controlla automaticamente l'integrità dei dati, quindi devi assicurarti che i dati soddisfino i vincoli specificati nello schema della tabella. Se non mantieni l'integrità dei dati nelle tabelle con vincoli specificati, i risultati della query potrebbero non essere accurati.
Passaggi successivi
- Scopri come ottimizzare i costi.
- Scopri come ottimizzare lo spazio di archiviazione.
- Scopri come ottimizzare le funzioni.