Introdução à API BigQuery Storage Write

A API BigQuery Storage Write é uma API de ingestão de dados unificada para o BigQuery. Ele combina ingestão de streaming e carregamento em lote em uma única API de alto desempenho. É possível usar a API StorageWrite para fazer streaming de registros para o BigQuery em tempo real ou para processar em lote um número arbitrariamente grande de registros e confirmá-los em uma única operação atômica.

Vantagens de usar a API Storage Write

Semântica de envio exatamente uma vez. A API Storage Write é compatível com semântica de exatamente uma vez por meio de deslocamentos de stream. Diferentemente do método tabledata.insertAll, a API Storage Write nunca grava duas mensagens que têm o mesmo deslocamento em um stream, se o cliente fornecer deslocamentos de stream ao anexar registros.

Transações no nível do stream. É possível gravar dados em um stream e confirmar os dados como uma única transação. Se a operação de confirmação falhar, será possível repetir a operação com segurança.

Transações em vários streams. Vários workers podem criar os próprios streams para processar dados de maneira independente. Quando todos os workers forem concluídos, será possível confirmar todos os streams como uma transação.

Protocolo eficiente. A API Storage Write é mais eficiente do que o método insertAll antigo porque usa streaming de gRPC em vez de REST sobre HTTP. A Storage Write API também é compatível com formatos binários na forma de buffers de protocolo, que são um formato eletrônico mais eficiente que o JSON. As solicitações de gravação são assíncronas com ordenação garantida.

Detecção de atualização de esquema. Se o esquema da tabela subjacente mudar enquanto o cliente estiver fazendo streaming, a Storage Write API notificará o cliente. O cliente pode decidir se quer se reconectar usando o esquema atualizado ou continuar gravando na conexão atual.

Menor custo. A API Storage Write tem um custo significativamente menor que a API de streaming insertAll mais antiga. Além disso, é possível ingerir até 2 TiB por mês gratuitamente.

Permissões necessárias

Para usar a API Storage Write, você precisa ter permissões bigquery.tables.updateData.

Os papéis predefinidos de gerenciamento de identidade e acesso (IAM, na sigla em inglês) incluem permissões bigquery.tables.updateData:

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

Para mais informações sobre os papéis e as permissões do IAM no BigQuery, consulte Papéis e permissões predefinidos.

Escopos de autenticação

O uso da API Storage Write requer um dos seguintes escopos do OAuth:

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

Para saber mais, consulte a Visão geral da autenticação.

Visão geral da API Storage Write

A abstração principal na API Storage Write é um stream. Um stream grava dados em uma tabela do BigQuery. Mais de um fluxo pode gravar simultaneamente na mesma tabela.

Stream padrão

A API StorageWrite fornece um stream padrão projetado para cenários em que você recebe dados continuamente. Ela tem as seguintes características:

  • Os dados gravados no stream padrão são disponibilizados imediatamente para consulta.
  • O stream padrão é compatível com a semântica do tipo "pelo menos uma vez".
  • Não é necessário criar o stream padrão explicitamente.

Se você estiver migrando da API tabledata.insertall legada, considere usar o fluxo padrão. Ele tem uma semântica de gravação semelhante, com maior resiliência de dados e menos restrições de escalonamento.

Fluxo da API:

  1. AppendRows (loop)

Para mais informações e código de exemplo, consulte Usar o fluxo padrão para semântica de pelo menos uma vez.

Fluxos criados pelo aplicativo

Você poderá criar um stream explicitamente se precisar de um dos seguintes comportamentos:

  • Exatamente uma vez a gravação por meio do uso de deslocamentos de stream.
  • Compatibilidade com outras propriedades ACID.

Em geral, os streams criados pelo aplicativo oferecem mais controle sobre a funcionalidade, à custa de mais complexidade.

Ao criar um stream, especifique um tipo. O tipo controla quando os dados gravados no stream ficam visíveis no BigQuery para leitura.

Tipo pendente

No tipo pendente, os registros são armazenados em buffer em estado pendente até que você confirme o stream. Quando você confirma um stream, todos os dados pendentes ficam disponíveis para leitura. A confirmação é uma operação atômica. Use esse tipo para cargas de trabalho em lote, como uma alternativa para jobs de carregamento do BigQuery. Para mais informações, consulte Carregar dados em lote usando a API StorageWrite.

Fluxo da API:

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

Tipo de compromisso

No tipo de compromisso, os registros ficam disponíveis para leitura imediatamente conforme você os grava no stream. Use esse tipo para cargas de trabalho de streaming que precisam de latência de leitura mínima. O stream padrão usa uma forma pelo menos uma vez do tipo confirmado. Para mais informações, consulte Usar o tipo confirmado para a semântica exatamente uma vez.

Fluxo da API:

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

Tipo armazenado em buffer

O tipo armazenado em buffer é um tipo avançado que no geral não é usado, exceto com o conector de E/S do BigQuery para o Apache Beam. Se você quiser garantir que os lotes pequenos sejam exibidos juntos, use o tipo confirmado e envie cada um deles em uma solicitação. Nesse tipo, as confirmações no nível da linha são fornecidas e os registros são armazenados em buffer até que as linhas sejam confirmadas pela transferência do stream.

Fluxo da API:

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

Selecionar um tipo

Use o fluxograma a seguir para ajudar a decidir qual é o melhor tipo para sua carga de trabalho:

image

Detalhes da API

Considere o seguinte ao usar a API Storage Write:

AppendRows

O método AppendRows anexa um ou mais registros ao stream. A primeira chamada para AppendRows precisa conter um nome de stream com o esquema de dados, especificado como DescriptorProto. Como prática recomendada, envie um lote de linhas em cada chamada de AppendRows. Não envie uma linha por vez.

Tratamento do buffer de proto

Os buffers de protocolo fornecem um mecanismo extensível que é neutro em relação à linguagem e à plataforma, para serializar dados estruturados de maneira compatível com versões futuras e anteriores. Eles são vantajosos porque oferecem armazenamento de dados compacto com análise rápida e eficiente. Para saber mais sobre buffers de protocolo, consulte Visão geral do buffer de protocolo.

Se você consumir a API diretamente com uma mensagem de buffer de protocolo predefinida, essa mensagem não poderá usar um especificador package e todos os tipos aninhados ou de enumeração precisarão ser definidos na mensagem raiz de nível superior. Referências a mensagens externas não são permitidas. Para ver um exemplo, consulte sample_data.proto.

Os clientes Java e Go são compatíveis com buffers de protocolo arbitrários porque a biblioteca de cliente normaliza o esquema de buffer de protocolo.

FinalizeWriteStream

O método FinalizeWriteStream finaliza o fluxo para que nenhum novo dado possa ser anexado a ele. Esse método é obrigatório no tipo Pending e opcional em Committed e Buffered. O stream padrão não é compatível com esse método.

Tratamento de erros

Se ocorrer um erro, o google.rpc.Status retornado poderá incluir um StorageError nos detalhes do erro. Consulte StorageErrorCode para encontrar o tipo de erro específico. Para mais informações sobre o modelo de erro da API do Google, consulte Erros.

Conexões

A API StorageWrite é uma API gRPC que usa conexões bidirecionais. O método AppendRows cria uma conexão com um stream. Você pode abrir várias conexões no fluxo padrão. Esses anexos são assíncronos, o que permite enviar uma série de gravações simultaneamente. As mensagens de resposta em cada conexão bidirecional chegam na mesma ordem que as solicitações foram enviadas.

Os streams criados pelo aplicativo podem ter apenas uma conexão ativa. Como prática recomendada, limite o número de conexões ativas e use uma conexão para o maior número possível de gravações de dados. Ao usar o stream padrão em Java ou Go, é possível usar a multiplexação da API Storage Write para gravar em várias tabelas de destino com conexões compartilhadas.

Geralmente, uma única conexão é compatível com pelo menos 1 MBps de capacidade de processamento. O limite superior depende de vários fatores, como largura de banda da rede, esquema dos dados e carga do servidor. Quando uma conexão atinge o limite de capacidade de processamento, as solicitações recebidas podem ser rejeitadas ou colocadas em fila até que o número de solicitações em trânsito diminua. Se você precisar de mais capacidade de processamento, crie mais conexões.

O BigQuery fechará a conexão gRPC se ela permanecer inativa por muito tempo. Se isso acontecer, o código de resposta será HTTP 409. A conexão gRPC também pode ser fechada no caso de uma reinicialização do servidor ou por outros motivos. Se ocorrer um erro de conexão, crie uma nova conexão. As bibliotecas de cliente Java e Go serão reconectadas automaticamente se a conexão for encerrada.

Suporte à biblioteca de cliente

Use a API Storage Write chamando a API gRPC diretamente ou usando uma das bibliotecas de cliente, que estão disponíveis para Java, Python e Go. Em geral, recomendamos o uso de uma biblioteca de cliente, porque ela fornece uma interface de programação mais simples e gerencia a RPC de streaming bidirecional subjacente para você.

Cliente Java

A biblioteca de cliente fornece dois objetos de gravação:

  • StreamWriter: aceita dados no formato de buffer de protocolo.

  • JsonStreamWriter: aceita dados no formato JSON e os converte em buffers de protocolo antes de enviá-los pela rede. O JsonStreamWriter também é compatível com atualizações automáticas de esquema. Se o esquema da tabela mudar, a gravação será reconectada automaticamente com o novo esquema, permitindo que o cliente envie dados usando o novo esquema.

O modelo de programação é semelhante para os dois escritores. A principal diferença é como você formata o payload.

O objeto gravador gerencia uma conexão da API StorageWrite. O objeto de gravação limpa automaticamente as solicitações, adiciona os cabeçalhos de roteamento regionais às solicitações e se reconecta após os erros de conexão. Caso você use a API gRPC diretamente, será necessário processar esses detalhes.

Cliente Python

O cliente Python é um cliente de nível inferior que encapsula a API gRPC. Para usar esse cliente, é necessário enviar os dados como buffers de protocolo, conforme descrito em Fluxo da API.

Para saber mais sobre o uso de buffers de protocolo com o Python, leia as Noções básicas do buffer de protocolo no Python.

Cliente Go

O cliente Go usa uma arquitetura de cliente-servidor para codificar mensagens no formato de buffer de protocolo usando proto2. Consulte a documentação do Go para conferir detalhes sobre como usar o cliente Go com um exemplo de código.

Conversões de tipo de dados

Veja na tabela a seguir os tipos de buffer de protocolo compatíveis para cada tipo de dados do BigQuery:

Tipo de dados BigQuery Tipos de buffer de protocolo compatíveis
BOOL bool, int32, int64, uint32, uint64, google.protobuf.BoolValue
BYTES bytes, string, google.protobuf.BytesValue
DATE int32 (recomendável), int64

O valor é o número de dias desde a época Unix (1970-01-01). O intervalo válido é de `-719162` (0001-01-01) a `2932896` (9999-12-31).

DATETIME, TIME string

O valor precisa ser um literal DATETIME ou TIME.

int64

Use a classe CivilTimeEncoder para realizar a conversão.

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

O valor é uma geometria no formato WKT ou 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

Use a classe BigDecimalByteStringEncoder para realizar a conversão.

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

O valor precisa ser um TIMEliteral.

TIMESTAMP int64 (recomendável), int32, uint32, google.protobuf.Timestamp

O valor é fornecido em microssegundos desde a época Unix (1970-01-01).

INTERVAL string, google.protobuf.Duration

O valor da string precisa ser um literal INTERVAL.

RANGE<T> (visualização) message

Um tipo de mensagem aninhado no proto com dois campos, start e end, em que os dois campos precisam ser do mesmo tipo de buffer de protocolo compatível que corresponde a um tipo de dados do BigQuery T. T precisa ser DATE, DATETIME ou TIMESTAMP. Se um campo (start ou end) não for definido na mensagem proto, ele vai representar um limite ilimitado. No exemplo a seguir, f_range_date representa uma coluna RANGE em uma tabela. Como o campo end não está definido na mensagem proto, o limite final desse intervalo é ilimitado.



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

Um tipo de matriz no proto corresponde a um campo repetido no BigQuery.

RECORD message

Um tipo de mensagem aninhada no proto corresponde a um campo de registro no BigQuery.

Gerenciar indisponibilidade

Tentar novamente com espera exponencial pode reduzir erros aleatórios e breves períodos de indisponibilidade do serviço, mas evitar a queda de linhas durante uma indisponibilidade prolongada requer mais atenção. Em especial, se um cliente não conseguir inserir uma linha de jeito nenhum, o que deverá fazer?

A resposta depende dos requisitos. Por exemplo, se o BigQuery estiver sendo usado para análises operacionais em que algumas linhas ausentes são aceitáveis, o cliente poderá desistir após algumas tentativas e descartar os dados. Se, por outro lado, todas as linhas forem essenciais para a empresa, como no caso de dados financeiros, será necessário ter uma estratégia para manter os dados até que possam ser inseridos mais tarde.

Uma forma comum de lidar com erros persistentes é publicar as linhas em um tópico do Pub/Sub para avaliação posterior e possível inserção. Outro método comum é a permanência temporária dos dados no cliente. Os dois métodos podem manter os clientes desbloqueados e, ao mesmo tempo, garantir que todas as linhas sejam inseridas quando a disponibilidade for restaurada.

Particionamento de colunas por unidade de tempo

É possível fazer streaming de dados em uma tabela particionada em uma coluna DATE, DATETIME ou TIMESTAMP que esteja entre os últimos cinco anos e o próximo ano. Dados fora desse intervalo são rejeitados.

Quando os dados são transmitidos, eles são inicialmente colocados na partição __UNPARTITIONED__. Depois que forem coletados dados não particionados suficientes, o BigQuery reparticionará os dados, colocando-os na partição apropriada. No entanto, não há contrato de nível de serviço (SLA, na sigla em inglês) que define quanto tempo leva para que esses dados sejam removidos da partição __UNPARTITIONED__.

A API Storage Write não é compatível com o uso de decoradores de partição.

Métricas da API Storage Write

Para métricas que monitoram a ingestão de dados com a API Storage Write, como a latência do lado do servidor, conexões simultâneas, bytes enviados e linhas enviadas, consulte Métricas do Google Cloud.

Usar linguagem de manipulação de dados (DML) com dados transmitidos recentemente

Use a linguagem de manipulação de dados (DML), como as instruções UPDATE, DELETE ou MERGE, para modificar as linhas que foram gravadas recentemente em uma tabela do BigQuery API Storage Write. Gravações recentes são aquelas que ocorreram nos últimos 30 minutos.

Para mais informações sobre como usar a DML para modificar os dados transmitidos, consulte Como usar a linguagem de manipulação de dados.

Cotas da API Storage Write

Para informações sobre as cotas e os limites da API Storage Write, consulte esta página.

É possível monitorar suas conexões simultâneas e o uso da cota de capacidade na página de Cotas do Console do Google Cloud.

Calcular a capacidade de processamento

Suponha que sua meta seja coletar registros de 100 milhões de endpoints, criando um registro de 1.500 por minuto. Em seguida, estime a capacidade como 100 million * 1,500 / 60 seconds = 2.5 GB per second. Você precisa garantir com antecedência que tem uma cota adequada para disponibilizar essa capacidade de processamento.

Preços da API Storage Write

Para preços, consulte Preços de ingestão de dados.

Exemplo de caso de uso:

Suponha que haja um pipeline processando dados de eventos de registros de endpoint. Os eventos são gerados continuamente e precisam estar disponíveis para consulta no BigQuery o mais rápido possível. Como a atualização de dados é fundamental para esse caso de uso, a API Storage Write é a melhor opção para ingerir dados no BigQuery. Uma arquitetura recomendada para manter esses endpoints enxutos é o envio de eventos para o Pub/Sub, de onde são consumidos por um pipeline de streaming do Dataflow que faz streaming diretamente para o BigQuery.

Uma das principais preocupações de confiabilidade dessa arquitetura é como lidar com uma falha ao inserir um registro no BigQuery. Se cada registro for importante e não puder ser perdido, os dados precisarão ser armazenados em buffer antes de tentar inserir. Na arquitetura recomendada acima, o Pub/Sub pode desempenhar o papel de buffer com recursos de retenção de mensagens. O pipeline do Dataflow precisa ser configurado para repetir inserções de streaming do BigQuery com espera exponencial truncada. Depois que a capacidade do Pub/Sub como buffer se esgotar, como no caso de indisponibilidade prolongada do BigQuery ou uma falha na rede, os dados precisarão ser mantidos no cliente, que vai precisar de um mecanismo para retomar a inserção de registros persistentes depois que a disponibilidade for restaurada. Saiba mais sobre como lidar com essa situação na postagem do blog Guia de confiabilidade do Google Pub/Sub.

Outro caso de falha a ser resolvido é o de um registro contaminado. Um registro contaminado é um registro rejeitado pelo BigQuery por ter falhado na inserção com um erro não repetível ou um registro que não foi inserido com sucesso após o número máximo de novas tentativas. Os dois tipos de registro precisam ser armazenados em uma "fila de mensagens inativas" pelo pipeline do Dataflow para uma investigação mais detalhada.

Se uma semântica for necessária exatamente uma vez, crie um fluxo de gravação em tipo confirmado, com deslocamentos de registro fornecidos pelo cliente. Isso evita duplicações, já que a operação de gravação só será realizada se o valor de deslocamento corresponder ao próximo deslocamento de anexo. Não fornecer um deslocamento significa que os registros são anexados à atual ponta do stream e repetir um anexo com falha pode fazer com que o registro apareça mais de uma vez no stream.

Se as garantias de "exatamente uma vez" não forem necessárias, grave no stream padrão. Isso permitirá maior capacidade e não será contabilizado no limite de cota na criação de streams de gravação.

Estime a capacidade da sua rede e verifique antecipadamente se você tem uma cota adequada para disponibilizar essa capacidade.

Se a carga de trabalho estiver gerando ou processando dados a uma taxa muito desigual, tente suavizar os picos de carga no cliente e fazer streaming no BigQuery com uma capacidade constante. Isso pode simplificar o planejamento da capacidade. Se isso não for possível, prepare-se para lidar com erros do tipo 429 (recurso esgotado) se e quando sua capacidade exceder a cota durante picos curtos.

A seguir