Introduzione all'API BigQuery Storage Scrivi

L'API BigQuery StorageWrite è 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 Writer per trasmettere flussi di record in BigQuery in tempo reale o per elaborare in batch un numero arbitrariamente elevato di record ed eseguirne il commit in un'unica operazione atomica.

Vantaggi dell'utilizzo dell'API Storage Scrivi

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

Transazioni a livello di stream. Puoi scrivere i dati in un flusso ed eseguirne il commit dei dati come singola transazione. Se l'operazione di commit non va a buon fine, puoi ritentare 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 StorageWrite è più efficiente del precedente metodo insertAll perché utilizza lo streaming gRPC anziché REST su HTTP. L'API StorageWrite supporta anche formati binari sotto forma di buffer di protocollo, che sono un formato di cavo più efficiente rispetto a JSON. Le richieste di scrittura sono asincrone con l'ordine garantito.

Rilevamento degli aggiornamenti dello schema. Se lo schema della tabella sottostante cambia mentre il client è in modalità flusso, l'API StorageWrite informa il client. Il client può decidere se riconnettersi utilizzando lo schema aggiornato o continuare a scrivere nella connessione esistente.

Riduzione dei costi. L'API StorageWrite ha un costo notevolmente inferiore rispetto alla precedente API Streaming insertAll. Inoltre, puoi importare gratuitamente fino a 2 TiB al mese.

Autorizzazioni obbligatorie

Per utilizzare l'API StorageWrite, 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 su ruoli e autorizzazioni IAM in BigQuery, vedi Ruoli e autorizzazioni predefiniti.

Ambiti di autenticazione

L'utilizzo dell'API StorageWrite 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 StorageWrite

L'astrazione principale dell'API StorageWrite è un flusso. Un flusso scrive dati in una tabella BigQuery. Più flussi possono scrivere contemporaneamente nella stessa tabella.

Stream predefinito

L'API StorageWrite fornisce un flusso predefinito, progettato per gli scenari di flusso in cui i dati sono in arrivo in modo continuo. Ha le seguenti caratteristiche:

  • I dati scritti nel flusso predefinito sono immediatamente disponibili per la query.
  • Il flusso predefinito supporta la semantica "at-least-once".
  • 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 il flusso predefinito per la semantica "at-least-once".

Flussi creati dall'applicazione

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

  • Scrivere la semantica "exactly-once" mediante l'uso di offset di flusso.
  • Supporto di altre proprietà ACID.

In generale, i flussi creati dall'applicazione offrono un maggiore controllo sulle funzionalità al costo di una maggiore complessità.

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

Tipo in attesa

Nel tipo In attesa, i record vengono inseriti nel buffer nello stato In attesa fino a quando non esegui il commit del 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 in batch, in alternativa ai job di caricamento BigQuery. Per maggiori informazioni, consulta Caricamento in batch dei dati con l'API StorageWrite.

Flusso API:

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

Tipo di impegno

Con il 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. Il flusso predefinito utilizza una forma "at-least-once" di tipo commit. Per maggiori informazioni, consulta Utilizzare il tipo di commit per la semantica "exactly-once".

Flusso API:

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

Tipo di buffer

Il tipo con buffer è un tipo avanzato che in genere non deve essere utilizzato, tranne che con il connettore BigQuery I/O di Apache Beam. Se vuoi assicurarti che siano visualizzati batch di piccole dimensioni, utilizza il tipo impegnato e invia ogni batch in un'unica richiesta. In questo tipo, vengono forniti i commit a livello di riga e i record vengono inseriti nel buffer finché non viene eseguito il commit delle righe mediante il flush del flusso.

Flusso API:

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

Selezionare un tipo

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

immagine

Dettagli API

Quando utilizzi l'API StorageWrite, considera quanto segue:

AppendRows

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

Gestione del buffer protocollo

I buffer di protocollo forniscono un meccanismo estensibile e indipendente dal linguaggio e dalla piattaforma per serializzare dati strutturati in modo compatibile con il futuro e con le versioni precedenti. Sono vantaggiosi in quanto offrono archiviazione compatta dei dati con analisi rapida ed efficiente. Per saperne di più sui buffer di protocollo, consulta Panoramica dei buffer di protocollo.

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

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

FinalizeWriteStream

Il metodo FinalizeWriteStream finalizza il flusso in modo che non possano essere aggiunti nuovi dati. Questo metodo è obbligatorio nel tipo Pending e facoltativo nei tipi Committed e Buffered. Il flusso predefinito non supporta questo metodo.

Gestione degli errori

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

Connessioni

L'API StorageWrite è un'API gRPC che utilizza connessioni bidirezionali. Il metodo AppendRows crea una connessione a uno stream. Puoi aprire più connessioni nel flusso predefinito. Queste appendici sono asincrone, perciò puoi 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 singola connessione per il maggior numero possibile di scritture di dati. Se utilizzi il flusso predefinito in Java o Go, puoi utilizzare il multiplix dell'API Storage Scrivi per scrivere su 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 della 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 finché il numero di richieste in corso non diminuisce. Se hai bisogno di una velocità effettiva maggiore, 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, creane una nuova. Le librerie client Java e Go si riconnettono automaticamente se la connessione viene chiusa.

Supporto della libreria client

Le librerie client per l'API StorageWrite sono disponibili in più linguaggi di programmazione ed espongono i costrutti dell'API sottostanti basati su gRPC. Questa API sfrutta funzionalità avanzate come i flussi di dati bidirezionali, che potrebbero richiedere ulteriori lavori di sviluppo per il supporto. A tal fine, per questa API sono disponibili varie astrazioni di livello superiore che semplificano queste interazioni e riducono i problemi degli sviluppatori. Se possibile, consigliamo di sfruttare queste altre astrazioni di libreria.

Questa sezione fornisce ulteriori dettagli su linguaggi e librerie in cui agli sviluppatori sono state fornite funzionalità aggiuntive oltre all'API generata.

Per vedere gli esempi di codice relativi all'API StorageWrite, vedi qui.

Client Java

La libreria client Java fornisce due oggetti writer:

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

  • JsonStreamWriter: accetta i dati in formato JSON e li converte in buffer di protocollo prima di inviarli tramite 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 i dati utilizzando il nuovo schema.

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

L'oggetto writer gestisce una connessione all'API StorageWrite. L'oggetto writer esegue automaticamente la pulizia delle richieste, aggiunge le intestazioni di routing a livello di regione alle richieste e si riconnette dopo gli errori di connessione. Se usi direttamente l'API gRPC, devi gestire questi dettagli.

Client Go

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

Client Python

Il client Python è un client di livello inferiore che aggrega l'API gRPC. Per utilizzare questo client, devi inviare i dati come buffer di protocollo, seguendo il flusso dell'API per il tipo specificato.

Per ulteriori informazioni sull'utilizzo dei buffer di protocollo con Python, leggi il tutorial di base sul buffer di protocollo in Python.

Client NodeJS

La libreria del client NodeJS accetta input JSON e fornisce il supporto della riconnessione automatica. Consulta la documentazione per i dettagli su come utilizzare il client.

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 è compreso tra "-719162" (0001-01-01) e "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, google.protobuf.UInt32Value
JSON string
NUMERIC, BIGNUMERIC int32, int64, uint32, uint64, double, float, 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 (opzione preferita), int32, uint32, google.protobuf.Timestamp

Il valore è espresso in microsecondi 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, in cui entrambi i campi devono avere lo stesso tipo di buffer di protocollo supportato che corrisponde a un tipo di dati BigQuery T. T deve essere DATE, DATETIME o TIMESTAMP. Se un campo (start o end) non è impostato nel messaggio di protocollo, rappresenta un confine illimitato. Nell'esempio seguente, f_range_date rappresenta una colonna RANGE in una tabella. Poiché il campo end non è impostato nel messaggio di protocollo, 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à

Riprovare con il backoff esponenziale può ridurre gli errori casuali e i brevi periodi di indisponibilità del servizio, ma per evitare di perdere righe in caso di indisponibilità estesa è necessario pensarci più a fondo. In particolare, cosa deve fare se un client non riesce costantemente a inserire una riga?

La risposta dipende dai tuoi requisiti. Ad esempio, se BigQuery viene utilizzato per l'analisi operativa e alcune righe mancanti sono accettabili, il client può rinunciare dopo alcuni nuovi tentativi ed eliminare i dati. Se, invece, ogni riga è fondamentale per l'attività, ad esempio per i dati finanziari, è necessario avere una strategia per rendere persistenti i dati fino a quando non possono essere inseriti in un secondo momento.

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

Partizionamento delle colonne in base all'unità di tempo

Puoi inserire i dati in flussi in una tabella partizionata in base a una colonna DATE, DATETIME o TIMESTAMP con un periodo compreso tra 5 anni e 1 anno nel futuro. I dati che non rientrano in questo intervallo sono stati rifiutati.

Quando i dati vengono trasmessi in flusso, vengono inizialmente posizionati nella partizione __UNPARTITIONED__. Dopo aver raccolto un numero sufficiente di dati non partizionati, BigQuery ripartiziona i dati, inserendoli nella partizione appropriata. Tuttavia, non esiste un accordo sul livello del servizio (SLA) che definisce il tempo necessario per spostare i dati al di fuori della partizione __UNPARTITIONED__.

L'API StorageWrite non supporta l'utilizzo dei decoratori della partizione.

Metriche dell'API Storage Scrivi

Per le metriche per monitorare l'importazione dati con l'API Storage Scrivi, ad esempio latenza a livello di richiesta lato server, connessioni simultanee, byte caricati e righe caricate, consulta le metriche di Google Cloud.

Usare Data Manipulation Language (DML) con flussi di dati recenti

Puoi utilizzare il Data Manipulation Language (DML), ad esempio le istruzioni UPDATE, DELETE o MERGE, per modificare le righe scritte di recente in una tabella BigQuery dall'API BigQuery StorageWrite. 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 Utilizzo del Data Manipulation Language (DML).

Limitazioni

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

Quote dell'API Storage Scrivi

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

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

Calcola la velocità effettiva

Supponiamo che il tuo obiettivo sia raccogliere log da 100 milioni di endpoint creando un record di 1500 log 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 Scrivi

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

Caso d'uso di esempio

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

Un problema di affidabilità fondamentale per questa architettura è come affrontare l'impossibilità di inserire un record in BigQuery. Se ogni record è importante e non può essere perso, i dati devono essere inseriti nel buffer prima di tentare l'inserimento. Nell'architettura consigliata sopra, 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 gli inserimenti di flussi di dati BigQuery con backoff esponenziale troncato. Una volta esaurita la capacità di Pub/Sub come buffer, ad esempio in caso di prolungata indisponibilità di BigQuery o di errore di rete, i dati devono essere mantenuti sul client e quest'ultimo 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 all'affidabilità di Google Pub/Sub.

Un altro caso di errore da gestire è quello dei record di veleno. Un record di poileon è un record rifiutato da BigQuery perché non riesce a essere inserito con un errore non riprovabile o perché non è stato inserito correttamente dopo il numero massimo di nuovi tentativi. Entrambi i tipi di record devono essere archiviati in una "coda dei 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. Ciò evita i duplicati, poiché l'operazione di scrittura viene eseguita solo se il valore di offset corrisponde all'offset dell'aggiunta successiva. Se non viene fornito un offset, i record vengono aggiunti alla fine attuale del flusso e il tentativo di aggiunta non riuscita potrebbe far sì che il record venga visualizzato più di una volta nel flusso.

Se non sono necessarie garanzie "exactly-once", la scrittura nel flusso predefinito consente una velocità effettiva più elevata e non viene conteggiata ai fini del limite di quota per la creazione di flussi di scrittura.

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

Se il carico di lavoro genera o elabora i dati a una velocità molto irregolare, prova a ridurre eventuali picchi di carico sul client e a inviare flussi a BigQuery con una velocità effettiva costante. Questo può semplificare la pianificazione della capacità. Se questo non è possibile, assicurati di essere pronto a gestire gli errori 429 (risorse esaurite) se e quando la velocità effettiva supera la quota durante brevi picchi.

Passaggi successivi