Introduzione all'API BigQuery Storage Write

L'API BigQuery Storage Write è un'API di importazione dati unificata per BigQuery. Combina l'importazione di flussi di dati e il caricamento in batch in un'unica API ad alte prestazioni. Puoi utilizzare l'API Storage Write per trasmettere i flussi di record in BigQuery in tempo reale o per elaborare in batch un numero arbitrario di record ed eseguirne il commit in una singola operazione atomica.

Vantaggi dell'utilizzo dell'API Storage Write

Semantica della consegna "exactly-once". L'API Storage Write supporta la semantica "exactly-once" tramite l'uso di offset del flusso. A differenza del metodo tabledata.insertAll, l'API Storage Write non scrive mai due messaggi con lo stesso offset all'interno di un flusso, se il client fornisce offset del flusso quando aggiunge record.

Transazioni a livello di stream. Puoi scrivere dati in un flusso ed eseguire il commit dei dati come singola transazione. Se l'operazione di commit non va a buon fine, puoi riprovare a eseguire l'operazione.

Transazioni tra stream. Più worker possono creare i propri flussi per elaborare i dati in modo indipendente. Quando tutti i worker hanno terminato, puoi eseguire il commit di tutti i flussi come transazione.

Protocollo efficiente. L'API Storage Write è più efficiente del metodo insertAll precedente perché utilizza il flusso gRPC anziché REST su HTTP. L'API Storage Write supporta anche formati binari sotto forma di buffer di protocollo, che sono un formato di connessione più efficiente rispetto a JSON. Le richieste di scrittura sono asincrone con ordinamento garantito.

Rilevamento degli aggiornamenti dello schema. Se lo schema della tabella sottostante cambia durante il flusso di dati del client, l'API Storage Write invia una notifica al client. Il client può decidere se riconnettersi utilizzando lo schema aggiornato o continuare a scrivere nella connessione esistente.

Costo inferiore. L'API Storage Write ha un costo notevolmente inferiore rispetto alla precedente API per i flussi di dati insertAll. Inoltre, puoi importare fino a 2 TiB al mese gratuitamente.

Autorizzazioni obbligatorie

Per utilizzare l'API Storage Write, devi disporre delle autorizzazioni bigquery.tables.updateData.

I seguenti ruoli predefiniti di Identity and Access Management (IAM) includono le autorizzazioni bigquery.tables.updateData:

  • bigquery.dataEditor
  • bigquery.dataOwner
  • bigquery.admin

Per ulteriori informazioni sui ruoli e sulle autorizzazioni IAM in BigQuery, consulta Autorizzazioni e ruoli predefiniti.

Ambiti di autenticazione

L'utilizzo dell'API Storage Write richiede uno dei seguenti ambiti OAuth:

  • https://www.googleapis.com/auth/bigquery
  • https://www.googleapis.com/auth/cloud-platform
  • https://www.googleapis.com/auth/bigquery.insertdata

Per ulteriori informazioni, consulta la Panoramica dell'autenticazione.

Panoramica dell'API Storage Write

L'astrazione principale nell'API Storage Write è un flusso. Un flusso scrive i dati in una tabella BigQuery. È possibile scrivere contemporaneamente più di un flusso nella stessa tabella.

Stream predefinito

L'API Storage Write fornisce uno stream predefinito, progettato per scenari di flussi di dati in cui i dati arrivano continuamente. Ha le seguenti caratteristiche:

  • I dati scritti nello stream predefinito sono immediatamente disponibili per la query.
  • Il flusso predefinito supporta la semantica "at almeno una volta".
  • Non è necessario creare esplicitamente lo stream predefinito.

Se stai eseguendo la migrazione dall'API tabledata.insertall legacy, valuta la possibilità di utilizzare lo stream predefinito. Ha una semantica di scrittura simile, con una maggiore resilienza dei dati e meno restrizioni di scalabilità.

Flusso API:

  1. AppendRows (loop)

Per maggiori informazioni e codice di esempio, consulta Utilizzare lo stream predefinito per la semantica "at-least-once".

Flussi creati dall'applicazione

Puoi creare esplicitamente uno stream se hai bisogno di uno dei seguenti comportamenti:

  • Scrivi la semantica "exactly-once" tramite l'uso degli offset del flusso.
  • Supporto di proprietà ACID aggiuntive.

In generale, i flussi creati dall'applicazione offrono un maggiore controllo sulla funzionalità a scapito di una complessità aggiuntiva.

Quando crei uno stream, specifichi un tipo. Il tipo controlla quando i dati scritti nel flusso diventano visibili in BigQuery per la lettura.

Tipo in attesa

Se impostato su In attesa di tipo, il buffer dei record viene mantenuto in uno stato In attesa finché non commetti il flusso. Quando esegui il commit di un flusso, tutti i dati in attesa diventano disponibili per la lettura. Il commit è un'operazione atomica. Utilizza questo tipo per i carichi di lavoro batch, in alternativa ai job di caricamento BigQuery. Per ulteriori informazioni, consulta Caricamento dei dati in batch utilizzando l'API Storage Write.

Flusso API:

  1. CreateWriteStream
  2. AppendRows (loop)
  3. FinalizeWriteStream
  4. BatchCommitWriteStreams

Tipo di impegno

Nel tipo di commit, i record sono disponibili per la lettura immediatamente mentre li scrivi nel flusso. Utilizza questo tipo per i flussi di lavoro che richiedono una latenza di lettura minima. Lo stream predefinito utilizza una forma almeno una volta del tipo impegnato. Per ulteriori informazioni, consulta Utilizzare il tipo impegnato per la semantica "exactly-once".

Flusso API:

  1. CreateWriteStream
  2. AppendRows (loop)
  3. FinalizeWriteStream (facoltativo)

Tipo con buffer

Il tipo con buffer è un tipo avanzato che in genere non dovrebbe essere utilizzato, ad eccezione del connettore BigQuery I/O di Apache Beam. Se hai dei batch di piccole dimensioni che vuoi garantire che vengano visualizzati insieme, utilizza il tipo impegnato e invia ogni batch in un'unica richiesta. In questo tipo, vengono forniti commit a livello di riga e viene eseguito il buffer dei record fino a quando non viene eseguito il commit delle righe mediante lo svuotamento del flusso.

Flusso API:

  1. CreateWriteStream
  2. AppendRowsFlushRows (loop)
  3. FinalizeWriteStream (facoltativo)

Selezionare un tipo

Utilizza il seguente diagramma di flusso per decidere quale tipo è più adatto al tuo carico di lavoro:

immagine

Dettagli API

Quando utilizzi l'API Storage Write, tieni presente quanto segue:

AppendRows

Il metodo AppendRows aggiunge uno o più record al flusso. La prima chiamata a AppendRows deve contenere un nome stream insieme allo schema dei dati, specificato come DescriptorProto. Come best practice, invia un gruppo di righe in ogni chiamata AppendRows. Non inviare una riga alla volta.

Gestione del buffer di protocollo

I buffer di protocollo offrono un meccanismo estensibile e indipendente dal linguaggio e dalla piattaforma per la serializzazione dei dati strutturati in modo compatibile con le versioni precedenti e con le versioni precedenti. Offrono vantaggi in quanto offrono archiviazione dei dati compatta con analisi rapida ed efficiente. Per scoprire di più sui buffer di protocollo, consulta Panoramica sui buffer di protocollo.

Se utilizzerai direttamente l'API con un messaggio del buffer di protocollo predefinito, il messaggio del buffer di protocollo non può utilizzare uno specificatore package e tutti i tipi nidificati o di enumerazione devono essere definiti all'interno del messaggio principale di primo livello. Non sono consentiti riferimenti a messaggi esterni. Per un esempio, consulta sample_data.proto.

I client Java e Go supportano buffer di protocollo arbitrari, poiché la libreria client normalizza lo schema di buffer di protocollo.

FinalizeWriteStream

Il metodo FinalizeWriteStream finalizza il flusso in modo che non possa essere aggiunto nuovi dati. Questo metodo è obbligatorio per i tipi Pending e facoltativo nei tipi Committed e Buffered. Lo stream predefinito non supporta questo metodo.

Gestione degli errori

Se si verifica un errore, l'oggetto google.rpc.Status restituito può includere un StorageError nei dettagli dell'errore. Consulta il StorageErrorCode per trovare il tipo di errore specifico. Per saperne di più sul modello di errore dell'API di Google, consulta Errori.

Connessioni

L'API Storage Write è un'API gRPC che utilizza connessioni bidirezionali. Il metodo AppendRows crea una connessione a uno stream. Puoi aprire più connessioni nello stream predefinito. Queste aggiunte sono asincrone, consentendo di inviare una serie di scritture contemporaneamente. I messaggi di risposta su ogni connessione bidirezionale arrivano nello stesso ordine in cui sono state inviate le richieste.

I flussi creati dall'applicazione possono avere una sola connessione attiva. Come best practice, limita il numero di connessioni attive e utilizza una sola connessione per il maggior numero possibile di scritture di dati. Quando utilizzi il flusso predefinito in Java o Go, puoi utilizzare il multiplexing dell'API Storage Write per scrivere in più tabelle di destinazione con connessioni condivise.

In genere, una singola connessione supporta almeno 1 MBps di velocità effettiva. Il limite superiore dipende da diversi fattori, come la larghezza di banda di rete, lo schema dei dati e il carico del server. Quando una connessione raggiunge il limite di velocità effettiva, le richieste in entrata potrebbero essere rifiutate o messe in coda fino a quando il numero di richieste in corso non diminuisce. Se hai bisogno di una velocità effettiva superiore, crea più connessioni.

BigQuery chiude la connessione gRPC se rimane inattiva per troppo tempo. In questo caso, il codice di risposta è HTTP 409. La connessione gRPC può essere chiusa anche in caso di riavvio del server o per altri motivi. Se si verifica un errore di connessione, crea una nuova connessione. Le librerie client Java e Go si riconnettono automaticamente se la connessione viene chiusa.

Supporto delle librerie client

Puoi utilizzare l'API Storage Write chiamando l'API gRPC direttamente o utilizzando una delle librerie client disponibili per Java, Python e Go. In generale, consigliamo di utilizzare una libreria client, perché fornisce un'interfaccia di programmazione più semplice e gestisce l'RPC di flusso bidirezionale sottostante per te.

Client Java

La libreria client Java fornisce due oggetti writer:

  • StreamWriter: accetta dati nel formato di buffer del protocollo.

  • JsonStreamWriter: accetta i dati in formato JSON e li converte in buffer di protocollo prima di inviarli via cavo. JsonStreamWriter supporta anche gli aggiornamenti automatici dello schema. Se lo schema della tabella cambia, il writer si riconnette automaticamente al nuovo schema, consentendo al client di inviare dati utilizzando il nuovo schema.

Il modello di programmazione è simile per entrambi gli autori. La differenza principale è il formato del payload.

L'oggetto writer gestisce una connessione all'API Storage Write. L'oggetto writer ripulisce automaticamente le richieste, aggiunge le intestazioni di routing regionali alle richieste e si riconnette in seguito a errori di connessione. Se usi direttamente l'API gRPC, devi gestire questi dettagli.

Client Python

Il client Python è un client di livello inferiore che esegue il wrapping dell'API gRPC. Per utilizzare questo client, devi inviare i dati sotto forma di buffer di protocollo, come descritto nel flusso API.

Per scoprire di più sull'utilizzo dei buffer di protocollo con Python, leggi il tutorial di base sul buffer di protocollo nel tutorial Python.

Client Go

Il client Go utilizza un'architettura client-server per codificare i messaggi all'interno del formato di buffer di protocollo utilizzando proto2. Per i dettagli su come utilizzare il client Go, con codice di esempio, consulta la documentazione di Go.

Conversioni dei tipi di dati

La seguente tabella mostra i tipi di buffer di protocollo supportati per ogni tipo di dati BigQuery:

Tipo di dati BigQuery Tipi di buffer di protocollo supportati
BOOL bool, int32, int64, uint32, uint64 e google.protobuf.BoolValue
BYTES bytes, string, google.protobuf.BytesValue
DATE int32 (opzione preferita), int64

Il valore è il numero di giorni dall'epoca di Unix (1970-01-01). L'intervallo valido è da "-719162" (0001-01-01) a "2932896" (9999-12-31).

DATETIME, TIME string

Il valore deve essere un valore letterale DATETIME o TIME.

int64

Utilizza la classe CivilTimeEncoder per eseguire la conversione.

FLOAT double, float, google.protobuf.DoubleValue, google.protobuf.FloatValue
GEOGRAPHY string

Il valore è una geometria in formato WKT o GeoJson.

INTEGER int32, int64, uint32, enum, google.protobuf.Int32Value, google.protobuf.Int64Value e google.protobuf.UInt32Value
JSON string
NUMERIC, BIGNUMERIC int32, int64, uint32, uint64, double, float e string
bytes, google.protobuf.BytesValue

Utilizza la classe BigDecimalByteStringEncoder per eseguire la conversione.

STRING string, enum, google.protobuf.StringValue
TIME string

Il valore deve essere un valore letterale TIME.

TIMESTAMP int64 (preferito), int32, uint32, google.protobuf.Timestamp

Il valore è espresso in microsecondi a partire dall'epoca di Unix (1970-01-01).

INTERVAL string, google.protobuf.Duration

Il valore della stringa deve essere un valore letterale INTERVAL.

RANGE<T> (anteprima) message

Un tipo di messaggio nidificato nel protocollo con due campi, start e end, dove entrambi i campi devono avere lo stesso tipo di buffer di protocollo supportato corrispondente a un tipo di dati BigQuery T. T deve essere DATE, DATETIME o TIMESTAMP. Se nel messaggio proto non è impostato un campo (start o end), rappresenta un confine non limitato. Nell'esempio seguente, f_range_date rappresenta una colonna RANGE in una tabella. Poiché il campo end non è impostato nel messaggio proto, il limite finale di questo intervallo è illimitato.


{
  f_range_date: {
    start: 1
  }
}
REPEATED FIELD array

Un tipo di array nel protocollo corrisponde a un campo ripetuto in BigQuery.

RECORD message

Un tipo di messaggio nidificato nel protocollo corrisponde a un campo di record in BigQuery.

Gestire la mancata disponibilità

Un nuovo tentativo con un backoff esponenziale può mitigare errori casuali e brevi periodi di indisponibilità del servizio, ma per evitare di perdere righe durante un'indisponibilità estesa richiede un'analisi più approfondita. In particolare, se un client non riesce sempre a inserire una riga, cosa deve fare?

La risposta dipende dalle tue esigenze. Ad esempio, se BigQuery viene utilizzato per l'analisi operativa e sono accettabili alcune righe mancanti, il cliente può rinunciare dopo alcuni tentativi ed eliminare i dati. Se, invece, ogni riga è cruciale per l'attività, ad esempio per i dati finanziari, devi definire una strategia per conservare i dati fino a quando non potranno essere inseriti in un secondo momento.

Un modo comune per gestire errori persistenti è pubblicare le righe in un argomento Pub/Sub per la valutazione successiva e il possibile inserimento. Un altro metodo comune è mantenere temporaneamente i dati sul client. Entrambi i metodi possono mantenere i client sbloccati e allo stesso tempo garantire che tutte le righe possano essere inserite una volta ripristinata la disponibilità.

Partizionamento delle colonne in unità di tempo

Puoi inserire flussi di dati in una tabella partizionata in una colonna DATE, DATETIME o TIMESTAMP compresa tra 5 anni nel passato e 1 anno nel futuro. I dati al di fuori di questo intervallo vengono rifiutati.

Durante il flusso di dati, i dati vengono inizialmente posizionati nella partizione __UNPARTITIONED__. Dopo aver raccolto un numero sufficiente di dati non partizionati, BigQuery esegue la ripartizione dei dati e li inserisce nella partizione appropriata. Tuttavia, non esiste un accordo sul livello del servizio (SLA) che definisce il tempo necessario per lo spostamento dei dati dalla partizione __UNPARTITIONED__.

L'API Storage Write non supporta l'uso di decorator delle partizioni.

Metriche dell'API Storage Write

Per conoscere le metriche per monitorare l'importazione dati con l'API Storage Write, come la latenza a livello di richiesta lato server, le connessioni simultanee, i byte caricati e le righe caricate, consulta le metriche di Google Cloud.

Usa il Data Manipulation Language (DML) con i flussi di dati trasmessi di recente

Puoi utilizzare il Data Manipulation Language (DML), come le istruzioni UPDATE, DELETE o MERGE, per modificare le righe scritte di recente in una tabella BigQuery dall'API BigQuery Storage Write. Le scritture recenti sono quelle che si sono verificate negli ultimi 30 minuti.

Per ulteriori informazioni sull'utilizzo di DML per modificare i flussi di dati, consulta Utilizzare il linguaggio di manipolazione dei dati.

Limitazioni

  • Il supporto per l'esecuzione di istruzioni DML mutanti sui dati trasmessi di recente non si estende ai flussi di dati trasmessi utilizzando l'API per i flussi di dati insertAll.
  • L'esecuzione di istruzioni DML mutanti all'interno di una transazione a più istruzioni sui dati trasmessi di recente non è supportata.

Quote dell'API Storage Write

Per informazioni sulle quote e sui limiti dell'API Storage Write, consulta Quote e limiti dell'API BigQuery Storage Write.

Puoi monitorare le connessioni simultanee e l'utilizzo della quota di velocità effettiva nella pagina Quote della console Google Cloud.

Calcola la velocità effettiva

Supponiamo che il tuo obiettivo sia raccogliere log da 100 milioni di endpoint creando un record di log di 1500 al minuto. Quindi, puoi stimare la velocità effettiva come 100 million * 1,500 / 60 seconds = 2.5 GB per second. Devi assicurarti in anticipo di disporre di una quota adeguata per gestire questa velocità effettiva.

Prezzi dell'API Storage Write

Per i prezzi, consulta la pagina Prezzi dell'importazione dati.

Caso d'uso di esempio

Supponiamo che esista una pipeline che elabora dati sugli eventi dai log degli endpoint. Gli eventi vengono generati continuamente e devono essere disponibili per le query in BigQuery il prima possibile. Poiché l'aggiornamento dei dati è fondamentale per questo caso d'uso, l'API Storage Write è la scelta migliore per importare i dati in BigQuery. Un'architettura consigliata per mantenere sottili questi endpoint consiste nell'invio di eventi a Pub/Sub, da dove vengono utilizzati da una pipeline Dataflow in modalità flusso che trasmette direttamente in BigQuery.

Una delle principali preoccupazioni di affidabilità di questa architettura è il modo in cui gestire il mancato inserimento di un record in BigQuery. Se ogni record è importante e non può essere perso, i dati devono essere sottoposti a buffering prima di tentare l'inserimento. Nell'architettura consigliata sopra riportata, Pub/Sub può svolgere il ruolo di buffer con le sue funzionalità di conservazione dei messaggi. La pipeline Dataflow deve essere configurata in modo da riprovare a inserire flussi di dati BigQuery con backoff esponenziale troncato. Una volta esaurita la capacità di Pub/Sub come buffer, ad esempio in caso di indisponibilità prolungata di BigQuery o di un errore di rete, i dati devono essere resi persistenti sul client e il client ha bisogno di un meccanismo per riprendere l'inserimento dei record persistenti una volta ripristinata la disponibilità. Per ulteriori informazioni su come gestire questa situazione, consulta il post del blog della guida sull'affidabilità di Google Pub/Sub.

Un altro caso di errore da gestire è quello di un registro veleno. Un record poison è un record rifiutato da BigQuery perché non può essere inserito con un errore non ripetibile o un record che non è stato inserito correttamente dopo il numero massimo di nuovi tentativi. Entrambi i tipi di record devono essere archiviati in una "coda messaggi non recapitabili" dalla pipeline Dataflow per ulteriori indagini.

Se è richiesta la semantica "exactly-once", crea un flusso di scrittura in tipo impegnato, con gli offset del record forniti dal client. In questo modo si evitano duplicati, in quanto l'operazione di scrittura viene eseguita solo se il valore di offset corrisponde all'offset di aggiunta successivo. Se non viene fornito un offset, i record vengono aggiunti alla fine attuale del flusso e il nuovo tentativo di un'aggiunta non riuscita potrebbe comportare la visualizzazione del record più volte nel flusso.

Se le garanzie "exactly-once" non sono richieste, la scrittura nel flusso predefinito consente una velocità effettiva superiore e non viene conteggiata nel limite di quota per la creazione di flussi di scrittura.

Stima la velocità effettiva della tua rete e assicurati in anticipo di avere una quota adeguata per la velocità effettiva.

Se il tuo carico di lavoro genera o elabora dati a una velocità molto non uniforme, prova a risolvere eventuali picchi di carico sul client e trasmetti flussi a BigQuery con una velocità effettiva costante. Questo può semplificare la pianificazione della capacità. Se non è possibile, assicurati di essere in grado di gestire gli errori 429 (risorsa esaurita) se e quando la velocità effettiva supera la quota durante brevi picchi.

Passaggi successivi