Piano di query e tempistiche

All'interno dei job di query, BigQuery include informazioni di diagnostica sul piano di query e sui tempi. È simile alle informazioni fornite da istruzioni come EXPLAIN in altri sistemi di database e analisi. Queste informazioni possono essere recuperate dalle risposte dell'API di metodi come jobs.get.

Per le query che richiedono molto tempo, BigQuery aggiorna periodicamente queste statistiche. Questi aggiornamenti vengono eseguiti indipendentemente dalla frequenza con cui viene eseguito il polling dello stato del job, ma in genere non si verificano più di una volta ogni 30 secondi. Inoltre, i job di query che non utilizzano risorse di esecuzione, come le richieste di prova secca o i risultati che possono essere pubblicati dai risultati memorizzati nella cache, non includeranno le informazioni di diagnostica aggiuntive, anche se potrebbero essere presenti altre statistiche.

Sfondo

Quando BigQuery esegue un job di query, converte l'istruzione SQL dichiarativa in un grafo di esecuzione, suddiviso in una serie di fasi di query, che a loro volta sono composte da insiemi più granulari di passaggi di esecuzione. BigQuery sfrutta un'architettura parallela fortemente distribuita per eseguire queste query. Le fasi modellano le unità di lavoro che molti potenziali lavoratori possono eseguire in parallelo. Le fasi comunicano tra loro utilizzando un'architettura di ordinamento distribuita rapida.

All'interno del piano di query, i termini unità di lavoro e worker vengono utilizzati per trasmettere informazioni specifiche sul parallelismo. In altre parti di BigQuery, potresti trovare il termine slot, che è una representatione astratta di più aspetti dell'esecuzione delle query, tra cui risorse di calcolo, memoria e I/O. Le statistiche dei job di primo livello forniscono la stima del costo di singole query utilizzando la stima totalSlotMs della query con questo sistema di contabilità astratto.

Un'altra proprietà importante dell'architettura di esecuzione delle query è che è dinamica, il che significa che il piano di query può essere modificato durante l'esecuzione di una query. Le fasi introdotte durante l'esecuzione di una query vengono spesso utilizzate per migliorare la distribuzione dei dati tra i worker di query. Nei piani di query in cui si verifica questo problema, queste fasi sono in genere etichettate come fasi di ripartizione.

Oltre al piano di query, i job di query mostrano anche una sequenza temporale di esecuzione, che fornisce un conteggio delle unità di lavoro completate, in attesa e attive all'interno dei worker di query. Una query può avere più fasi contemporaneamente con worker attivi e la sequenza temporale è progettata per mostrare l'avanzamento complessivo della query.

Visualizzazione delle informazioni con la console Google Cloud

Nella console Google Cloud, puoi visualizzare i dettagli del piano di query per una query completata facendo clic sul pulsante Dettagli esecuzione (vicino al pulsante Risultati).

Il piano di query.

Informazioni sul piano di query

Nella risposta dell'API, i piani di query sono rappresentati come un elenco di fasi di query. Ogni elemento dell'elenco mostra statistiche di panoramica per fase, informazioni dettagliate sui passaggi e classificazioni dei tempi di ciascuna fase. Non tutti i dettagli vengono visualizzati nella console Google Cloud, ma possono essere tutti presenti nelle risposte dell'API.

Panoramica della fase

I campi di panoramica per ogni fase possono includere quanto segue:

Campo API Descrizione
id ID numerico univoco per la fase.
name Nome di riepilogo semplice per la fase. I steps all'interno della fase forniscono ulteriori dettagli sui passaggi di esecuzione.
status Stato di esecuzione della fase. Gli stati possibili sono PENDING, RUNNING, COMPLETE, FAILED e CANCELLED.
inputStages Un elenco di ID che formano il grafico delle dipendenze della fase. Ad esempio, una fase JOIN spesso richiede due fasi dipendenti che preparano i dati sul lato sinistro e destro della relazione JOIN.
startMs Timestamp in millisecondi epoch che rappresenta il momento in cui il primo worker all'interno della fase ha iniziato l'esecuzione.
endMs Timestamp in millisecondi epoch che rappresenta il momento in cui l'ultimo worker ha completato l'esecuzione.
steps Elenco più dettagliato dei passaggi di esecuzione all'interno della fase. Per ulteriori informazioni, consulta la sezione successiva.
recordsRead Dimensioni di input della fase come numero di record in tutti i worker della fase.
recordsWritten Dimensione dell'output della fase come numero di record in tutti i worker della fase.
parallelInputs Numero di unità di lavoro parallelizzabili per la fase. A seconda della fase e della query, può rappresentare il numero di segmenti colonnari all'interno di una tabella o il numero di partizioni all'interno di un'operazione di mescolamento intermedio.
completedParallelInputs Numero di unità di lavoro completate nella fase. Per alcune query, non è necessario compilare tutti gli input all'interno di una fase per il completamento della fase.
shuffleOutputBytes Rappresenta i byte totali scritti su tutti i worker all'interno di una fase di query.
shuffleOutputBytesSpilled Le query che trasmettono dati significativi tra le fasi potrebbero dover ricorrere alla trasmissione basata su disco. La statistica dei byte con overflow indica la quantità di dati che sono stati trasferiti sul disco. Dipende da un algoritmo di ottimizzazione, pertanto non è deterministico per una determinata query.

Informazioni sui passaggi per fase

I passaggi rappresentano le operazioni più granulari che ogni worker all'interno di una fase deve eseguire, presentate come un elenco ordinato di operazioni. I passaggi sono classificati e alcune operazioni forniscono informazioni più dettagliate. Le categorie di operazioni presenti nel piano di query includono quanto segue:

Passaggio Descrizione
LEGGI Lettura di una o più colonne da una tabella di input o da uno shuffling intermedio. Nei dettagli del passaggio vengono restituite solo le prime sedici colonne lette.
SCRIVI Scrittura di una o più colonne in una tabella di output o in un risultato intermedio. Per gli output partizionati HASH di una fase, sono incluse anche le colonne utilizzate come chiave di partizione.
COMPUTING Operazioni come la valutazione delle espressioni e le funzioni SQL.
FILTRO Operatore che implementa le clausole WHERE, OMIT IF e HAVING.
SORT Operazione di ordinamento o Order By, incluse le chiavi di colonna e la direzione di ordinamento.
AGGREGATE Un'operazione di aggregazione, ad esempio GROUP BY o COUNT.
LIMIT Operatore che implementa la clausola LIMIT.
JOIN Un'operazione JOIN, che include il tipo di join e le colonne utilizzate.
ANALYTIC_FUNCTION Un'invocazione di una funzione finestra (detta anche "funzione analitica").
USER_DEFINED_FUNCTION Una chiamata a una funzione definita dall'utente.

Classificazione dei tempi per fase

Le fasi di query forniscono anche classificazioni dei tempi delle fasi, sia in forma relativa che assoluta. Poiché ogni fase di esecuzione rappresenta il lavoro intrapreso da uno o più lavoratori indipendenti, le informazioni vengono fornite sia in termini di tempo medio che di tempo peggiore. Questi tempi rappresentano il rendimento medio di tutti i worker in una fase, nonché il rendimento del worker più lento della coda lunga per una determinata classificazione. Inoltre, i tempi medi e massimi sono suddivisi in rappresentazioni absolute e relative. Per le statistiche basate su rapporti, i dati vengono forniti come frazione del tempo più lungo trascorso da qualsiasi worker in qualsiasi segmento.

La console Google Cloud presenta la tempistica delle fasi utilizzando le rappresentazioni della tempistica relativa.

Le informazioni sui tempi della fase vengono riportate come segue:

Tempi relativi Temporizzazione assoluta Numeratore del rapporto
waitRatioAvg waitMsAvg Tempo medio impiegato dal worker in attesa di essere pianificato.
waitRatioMax waitMsMax Tempo impiegato dal worker più lento in attesa di essere pianificato.
readRatioAvg readMsAvg Tempo impiegato dal lavoratore medio per leggere i dati di input.
readRatioMax readMsMax Tempo impiegato dal worker più lento per leggere i dati di input.
computeRatioAvg computeMsAvg Tempo impiegato dal worker medio per l'utilizzo della CPU.
computeRatioMax computeMsMax Tempo impiegato dal worker più lento per il vincolo della CPU.
writeRatioAvg writeMsAvg Tempo impiegato dal lavoratore medio per scrivere i dati di output.
writeRatioMax writeMsMax Tempo impiegato dal worker più lento per scrivere i dati di output.

Spiegazione delle query federate

Le query federate ti consentono di inviare un'istruzione di query a un'origine dati esterna utilizzando la funzione EXTERNAL_QUERY. Le query federate sono soggette alla tecnica di ottimizzazione nota come pushdown SQL e il piano di query mostra le operazioni spostate all'origine dati esterna, se presenti. Ad esempio, se esegui la seguente query:

SELECT id, name
FROM EXTERNAL_QUERY("<connection>", "SELECT * FROM company")
WHERE country_code IN ('ee', 'hu') AND name like '%TV%'

Il piano di query mostrerà i seguenti passaggi della fase:

$1:id, $2:name, $3:country_code
FROM table_for_external_query_$_0(
  SELECT id, name, country_code
  FROM (
    /*native_query*/
    SELECT * FROM company
  )
  WHERE in(country_code, 'ee', 'hu')
)
WHERE and(in($3, 'ee', 'hu'), like($2, '%TV%'))
$1, $2
TO __stage00_output

In questo piano, table_for_external_query_$_0(...) rappresenta la funzione EXTERNAL_QUERY. Tra parentesi puoi vedere la query eseguita dall'origine dati esterna. In base a ciò, puoi notare che:

  • Un'origine dati esterna restituisce solo tre colonne selezionate.
  • Un'origine dati esterna restituisce solo le righe per le quali country_code è 'ee' o 'hu'.
  • L'operatore LIKE non viene eseguito push down e viene valutato da BigQuery.

Per fare un confronto, se non sono presenti pushdown, il piano di query mostrerà i seguenti passaggi della fase:

$1:id, $2:name, $3:country_code
FROM table_for_external_query_$_0(
  SELECT id, name, description, country_code, primary_address, secondary address
  FROM (
    /*native_query*/
    SELECT * FROM company
  )
)
WHERE and(in($3, 'ee', 'hu'), like($2, '%TV%'))
$1, $2
TO __stage00_output

Questa volta un'origine dati esterna restituisce tutte le colonne e tutte le righe della tabella company e BigQuery esegue il filtro.

Metadati di Timeline

La cronologia delle query registra i progressi in momenti specifici, fornendo visualizzazioni istantanee dell'avanzamento complessivo delle query. La cronologia è rappresentata come una serie di campioni che riportano i seguenti dettagli:

Campo Descrizione
elapsedMs Millisecondi trascorsi dall'inizio dell'esecuzione della query.
totalSlotMs Una rappresentazione cumulativa dei millisecondi dello slot utilizzati dalla query.
pendingUnits Unità di lavoro totali pianificate e in attesa di esecuzione.
activeUnits Unità di lavoro attive totali in fase di elaborazione dai worker.
completedUnits Unità di lavoro totali completate durante l'esecuzione di questa query.

Un esempio di query

La seguente query conteggia il numero di righe nel set di dati pubblico di Shakespeare e ha un secondo conteggio condizionale che limita i risultati alle righe che fanno riferimento a "Amleto":

SELECT
  COUNT(1) as rowcount,
  COUNTIF(corpus = 'hamlet') as rowcount_hamlet
FROM `publicdata.samples.shakespeare`

Fai clic su Dettagli esecuzione per visualizzare il piano di query:

Il piano di query del villaggio.

Gli indicatori di colore mostrano i tempi relativi di tutti i passaggi in tutte le fasi.

Per scoprire di più sui passaggi delle fasi di esecuzione, fai clic su per espandere i dettagli della fase:

I dettagli del passaggio del piano di query del villaggio.

In questo esempio, il tempo più lungo in qualsiasi segmento è stato il tempo impiegato dal singolo worker nella fase 01 per attendere il completamento della fase 00. Questo perché la fase 01 dipendeva dall'input della fase 00 e non poteva iniziare fino a quando la prima fase non aveva scritto l'output nell'ordinamento intermedio.

Segnalazione degli errori

È possibile che i job di query non vadano a buon fine durante l'esecuzione. Poiché le informazioni sul piano vengono aggiornate periodicamente, puoi osservare dove si è verificato l'errore nel grafico di esecuzione. Nella console Google Cloud, le fasi riuscite o non riuscite sono contrassegnate da un segno di spunta o da un punto esclamativo accanto al nome della fase.

Per ulteriori informazioni sull'interpretazione e sulla correzione degli errori, consulta la guida alla risoluzione dei problemi.

Rappresentazione di un esempio di API

Le informazioni sul piano di query sono incorporate nelle informazioni sulla risposta al job e puoi recuperarle chiamando jobs.get. Ad esempio, il seguente estratto di una risposta JSON per un job che restituisce la query Hamlet di esempio mostra sia il piano di query sia le informazioni sulle tempistiche.

"statistics": {
  "creationTime": "1576544129234",
  "startTime": "1576544129348",
  "endTime": "1576544129681",
  "totalBytesProcessed": "2464625",
  "query": {
    "queryPlan": [
      {
        "name": "S00: Input",
        "id": "0",
        "startMs": "1576544129436",
        "endMs": "1576544129465",
        "waitRatioAvg": 0.04,
        "waitMsAvg": "1",
        "waitRatioMax": 0.04,
        "waitMsMax": "1",
        "readRatioAvg": 0.32,
        "readMsAvg": "8",
        "readRatioMax": 0.32,
        "readMsMax": "8",
        "computeRatioAvg": 1,
        "computeMsAvg": "25",
        "computeRatioMax": 1,
        "computeMsMax": "25",
        "writeRatioAvg": 0.08,
        "writeMsAvg": "2",
        "writeRatioMax": 0.08,
        "writeMsMax": "2",
        "shuffleOutputBytes": "18",
        "shuffleOutputBytesSpilled": "0",
        "recordsRead": "164656",
        "recordsWritten": "1",
        "parallelInputs": "1",
        "completedParallelInputs": "1",
        "status": "COMPLETE",
        "steps": [
          {
            "kind": "READ",
            "substeps": [
              "$1:corpus",
              "FROM publicdata.samples.shakespeare"
            ]
          },
          {
            "kind": "AGGREGATE",
            "substeps": [
              "$20 := COUNT($30)",
              "$21 := COUNTIF($31)"
            ]
          },
          {
            "kind": "COMPUTE",
            "substeps": [
              "$30 := 1",
              "$31 := equal($1, 'hamlet')"
            ]
          },
          {
            "kind": "WRITE",
            "substeps": [
              "$20, $21",
              "TO __stage00_output"
            ]
          }
        ]
      },
      {
        "name": "S01: Output",
        "id": "1",
        "startMs": "1576544129465",
        "endMs": "1576544129480",
        "inputStages": [
          "0"
        ],
        "waitRatioAvg": 0.44,
        "waitMsAvg": "11",
        "waitRatioMax": 0.44,
        "waitMsMax": "11",
        "readRatioAvg": 0,
        "readMsAvg": "0",
        "readRatioMax": 0,
        "readMsMax": "0",
        "computeRatioAvg": 0.2,
        "computeMsAvg": "5",
        "computeRatioMax": 0.2,
        "computeMsMax": "5",
        "writeRatioAvg": 0.16,
        "writeMsAvg": "4",
        "writeRatioMax": 0.16,
        "writeMsMax": "4",
        "shuffleOutputBytes": "17",
        "shuffleOutputBytesSpilled": "0",
        "recordsRead": "1",
        "recordsWritten": "1",
        "parallelInputs": "1",
        "completedParallelInputs": "1",
        "status": "COMPLETE",
        "steps": [
          {
            "kind": "READ",
            "substeps": [
              "$20, $21",
              "FROM __stage00_output"
            ]
          },
          {
            "kind": "AGGREGATE",
            "substeps": [
              "$10 := SUM_OF_COUNTS($20)",
              "$11 := SUM_OF_COUNTS($21)"
            ]
          },
          {
            "kind": "WRITE",
            "substeps": [
              "$10, $11",
              "TO __stage01_output"
            ]
          }
        ]
      }
    ],
    "estimatedBytesProcessed": "2464625",
    "timeline": [
      {
        "elapsedMs": "304",
        "totalSlotMs": "50",
        "pendingUnits": "0",
        "completedUnits": "2"
      }
    ],
    "totalPartitionsProcessed": "0",
    "totalBytesProcessed": "2464625",
    "totalBytesBilled": "10485760",
    "billingTier": 1,
    "totalSlotMs": "50",
    "cacheHit": false,
    "referencedTables": [
      {
        "projectId": "publicdata",
        "datasetId": "samples",
        "tableId": "shakespeare"
      }
    ],
    "statementType": "SELECT"
  },
  "totalSlotMs": "50"
},

Utilizzo delle informazioni sull'esecuzione

I piani di query di BigQuery forniscono informazioni su come il servizio esegue le query, ma la natura gestita del servizio limita la possibilità di applicare direttamente alcuni dettagli. Molte ottimizzazioni vengono eseguite automaticamente utilizzando il servizio, che può essere diverso da altri ambienti in cui la regolazione, il provisioning e il monitoraggio possono richiedere personale dedicato e competente.

Per tecniche specifiche che possono migliorare l'esecuzione e le prestazioni delle query, consulta la documentazione sulle best practice. Le statistiche del piano di query e della cronologia possono aiutarti a capire se determinate fasi predominano nell'utilizzo delle risorse. Ad esempio, una fase JOIN che genera molto più righe di output rispetto alle righe di input può indicare un'opportunità di applicare un filtro all'inizio della query.

Inoltre, le informazioni sulle tempistiche possono aiutarti a capire se una determinata query è lenta in modo isolato o a causa degli effetti di altre query in competizione per le stesse risorse. Se noti che il numero di unità attive rimane limitato per tutta la durata della query, ma la quantità di unità di lavoro in coda rimane elevata, puoi trovarti in un caso in cui la riduzione del numero di query simultanee può migliorare notevolmente il tempo di esecuzione complessivo per determinate query.