Caricamento di dati in batch utilizzando l'API Storage Write (gRPC)

Questo documento descrive come utilizzare l' API BigQuery Storage Write (gRPC) per caricare dati in batch in BigQuery.

Negli scenari di caricamento in batch, un'applicazione scrive i dati ed esegue il commit come singola transazione atomica. Quando utilizzi l'API Storage Write (gRPC) per caricare dati in batch, crea uno o più stream di tipo in attesa. Il tipo in attesa supporta le transazioni a livello di stream. I record vengono memorizzati in un buffer in stato di attesa finché non esegui il commit dello stream.

Per i carichi di lavoro batch, valuta anche la possibilità di utilizzare l'API Storage Write (gRPC) tramite il connettore Apache Spark SQL per BigQuery utilizzando Managed Service for Apache Spark, anziché scrivere codice personalizzato dell'API Storage Write (gRPC).

L'API Storage Write (gRPC) è adatta a un'architettura di pipeline di dati. Un processo principale crea una serie di stream. Per ogni stream, assegna un thread di lavoro o un processo separato per scrivere una parte dei dati batch. Ogni worker crea una connessione al proprio stream, scrive i dati e finalizza lo stream al termine. Dopo che tutti i worker segnalano il completamento riuscito al processo principale, quest'ultimo esegue il commit dei dati. Se un worker non riesce, la parte di dati assegnata non verrà visualizzata nei risultati finali e l'intero worker può essere ritentato in sicurezza. In una pipeline più sofisticata, i worker eseguono il checkpoint dei progressi segnalando l'ultimo offset scritto al processo principale. Questo approccio può portare a una pipeline robusta e resiliente agli errori.

Caricamento di dati in batch utilizzando il tipo in attesa

Per utilizzare il tipo in attesa, l'applicazione esegue le seguenti operazioni:

  1. Chiama CreateWriteStream per creare uno o più stream di tipo in attesa.
  2. Per ogni stream, chiama AppendRows in un loop per scrivere batch di record.
  3. Per ogni stream, chiama FinalizeWriteStream. Dopo aver chiamato questo metodo, non puoi scrivere altre righe nello stream. Se chiami AppendRows dopo aver chiamato FinalizeWriteStream, viene restituito un StorageError con StorageErrorCode.STREAM_FINALIZED nell'errore google.rpc.Status. Per saperne di più sul modello di errore google.rpc.Status, consulta la sezione Errori.
  4. Chiama BatchCommitWriteStreams per eseguire il commit degli stream. Dopo aver chiamato questo metodo, i dati diventano disponibili per la lettura. Se si verifica un errore durante il commit di uno degli stream, l'errore viene restituito nel stream_errors campo di BatchCommitWriteStreamsResponse.

Il commit è un'operazione atomica e puoi eseguire il commit di più stream contemporaneamente. È possibile eseguire il commit di uno stream una sola volta, quindi se l'operazione di commit non riesce, è sicuro riprovare. Finché non esegui il commit di uno stream, i dati sono in attesa e non visibili per le letture.

Dopo la finalizzazione dello stream e prima del commit, i dati possono rimanere nel buffer per un massimo di 4 ore. È necessario eseguire il commit degli stream in attesa entro 24 ore. Esiste un limite di quota per le dimensioni totali del buffer dello stream in attesa.

Il seguente codice mostra come scrivere i dati di tipo in attesa:

C#

Per scoprire come installare e utilizzare la libreria client per BigQuery, consulta Librerie client di BigQuery. Per saperne di più, consulta la documentazione di riferimento dell'C# API BigQuery.

Per eseguire l'autenticazione in BigQuery, configura le Credenziali predefinite dell'applicazione. Per saperne di più, vedi Configura l'autenticazione per le librerie client.


using Google.Api.Gax.Grpc;
using Google.Cloud.BigQuery.Storage.V1;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static Google.Cloud.BigQuery.Storage.V1.AppendRowsRequest.Types;

public class AppendRowsPendingSample
{
    /// <summary>
    /// This code sample demonstrates how to write records in pending mode.
    /// Create a write stream, write some sample data, and commit the stream to append the rows.
    /// The CustomerRecord proto used in the sample can be seen in Resources folder and generated C# is placed in Data folder in
    /// https://github.com/GoogleCloudPlatform/dotnet-docs-samples/tree/main/bigquery-storage/api/BigQueryStorage.Samples
    /// </summary>
    public async Task AppendRowsPendingAsync(string projectId, string datasetId, string tableId)
    {
        BigQueryWriteClient bigQueryWriteClient = await BigQueryWriteClient.CreateAsync();
        // Initialize a write stream for the specified table.
        // When creating the stream, choose the type. Use the Pending type to wait
        // until the stream is committed before it is visible. See:
        // https://cloud.google.com/bigquery/docs/reference/storage/rpc/google.cloud.bigquery.storage.v1#google.cloud.bigquery.storage.v1.WriteStream.Type
        WriteStream stream = new WriteStream { Type = WriteStream.Types.Type.Pending };
        TableName tableName = TableName.FromProjectDatasetTable(projectId, datasetId, tableId);

        stream = await bigQueryWriteClient.CreateWriteStreamAsync(tableName, stream);

        // Initialize streaming call, retrieving the stream object
        BigQueryWriteClient.AppendRowsStream rowAppender = bigQueryWriteClient.AppendRows();

        // Sending requests and retrieving responses can be arbitrarily interleaved.
        // Exact sequence will depend on client/server behavior.
        // Create task to do something with responses from server.
        Task appendResultsHandlerTask = Task.Run(async () =>
        {
            AsyncResponseStream<AppendRowsResponse> appendRowResults = rowAppender.GetResponseStream();
            while (await appendRowResults.MoveNextAsync())
            {
                AppendRowsResponse responseItem = appendRowResults.Current;
                // Do something with responses.
                if (responseItem.AppendResult != null)
                {
                    Console.WriteLine($"Appending rows resulted in: {responseItem.AppendResult}");
                }
                if (responseItem.Error != null)
                {
                    Console.Error.WriteLine($"Appending rows resulted in an error: {responseItem.Error.Message}");
                    foreach (RowError rowError in responseItem.RowErrors)
                    {
                        Console.Error.WriteLine($"Row Error: {rowError}");
                    }
                }
            }
            // The response stream has completed.
        });

        // List of records to be appended in the table.
        List<CustomerRecord> records = new List<CustomerRecord>
        {
            new CustomerRecord { CustomerNumber = 1, CustomerName = "Alice" },
            new CustomerRecord { CustomerNumber = 2, CustomerName = "Bob" }
        };

        // Create a batch of row data by appending serialized bytes to the
        // SerializedRows repeated field.
        ProtoData protoData = new ProtoData
        {
            WriterSchema = new ProtoSchema { ProtoDescriptor = CustomerRecord.Descriptor.ToProto() },
            Rows = new ProtoRows { SerializedRows = { records.Select(r => r.ToByteString()) } }
        };

        // Initialize the append row request.
        AppendRowsRequest appendRowRequest = new AppendRowsRequest
        {
            WriteStreamAsWriteStreamName = stream.WriteStreamName,
            ProtoRows = protoData
        };

        // Stream a request to the server.
        await rowAppender.WriteAsync(appendRowRequest);

        // Append a second batch of data.
        protoData = new ProtoData
        {
            Rows = new ProtoRows { SerializedRows = { new CustomerRecord { CustomerNumber = 3, CustomerName = "Charles" }.ToByteString() } }
        };

        // Since this is the second request, you only need to include the row data.
        // The name of the stream and protocol buffers descriptor is only needed in
        // the first request.
        appendRowRequest = new AppendRowsRequest
        {
            // If Offset is not present, the write is performed at the current end of stream.
            ProtoRows = protoData
        };

        await rowAppender.WriteAsync(appendRowRequest);

        // Complete writing requests to the stream.
        await rowAppender.WriteCompleteAsync();

        // Await the handler. This will complete once all server responses have been processed.
        await appendResultsHandlerTask;

        // A Pending type stream must be "finalized" before being committed. No new
        // records can be written to the stream after this method has been called.
        await bigQueryWriteClient.FinalizeWriteStreamAsync(stream.Name);
        BatchCommitWriteStreamsRequest batchCommitWriteStreamsRequest = new BatchCommitWriteStreamsRequest
        {
            Parent = tableName.ToString(),
            WriteStreams = { stream.Name }
        };

        BatchCommitWriteStreamsResponse batchCommitWriteStreamsResponse =
            await bigQueryWriteClient.BatchCommitWriteStreamsAsync(batchCommitWriteStreamsRequest);
        if (batchCommitWriteStreamsResponse.StreamErrors?.Count > 0)
        {
            // Handle errors here.
            Console.WriteLine("Error committing write streams. Individual errors:");
            foreach (StorageError error in batchCommitWriteStreamsResponse.StreamErrors)
            {
                Console.WriteLine(error.ErrorMessage);
            }            
        }
        else
        {
            Console.WriteLine($"Writes to stream {stream.Name} have been committed.");
        }
    }
}

Vai

Per scoprire come installare e utilizzare la libreria client per BigQuery, consulta Librerie client di BigQuery. Per saperne di più, consulta la documentazione di riferimento dell'Go API BigQuery.

Per eseguire l'autenticazione in BigQuery, configura le Credenziali predefinite dell'applicazione. Per saperne di più, vedi Configura l'autenticazione per le librerie client.


import (
	"context"
	"fmt"
	"io"
	"math/rand"
	"time"

	"cloud.google.com/go/bigquery/storage/apiv1/storagepb"
	"cloud.google.com/go/bigquery/storage/managedwriter"
	"cloud.google.com/go/bigquery/storage/managedwriter/adapt"
	"github.com/GoogleCloudPlatform/golang-samples/bigquery/snippets/managedwriter/exampleproto"
	"google.golang.org/protobuf/proto"
)

// generateExampleMessages generates a slice of serialized protobuf messages using a statically defined
// and compiled protocol buffer file, and returns the binary serialized representation.
func generateExampleMessages(numMessages int) ([][]byte, error) {
	msgs := make([][]byte, numMessages)
	for i := 0; i < numMessages; i++ {

		random := rand.New(rand.NewSource(time.Now().UnixNano()))

		// Our example data embeds an array of structs, so we'll construct that first.
		sList := make([]*exampleproto.SampleStruct,