Usar a API de streaming legada

Neste documento, descrevemos como fazer streaming de dados no BigQuery usando o método tabledata.insertAll legado.

Para novos projetos, recomendamos usar a API BigQuery Storage Write em vez do método tabledata.insertAll. A API Storage Write tem preços mais baixos e recursos mais robustos, incluindo semântica de entrega apenas uma vez. Se você estiver migrando um projeto atual do método tabledata.insertAll para a API Storage Write, recomendamos selecionar o fluxo padrão. O método tabledata.insertAll ainda é totalmente compatível.

Antes de começar

  1. Verifique se você tem acesso de gravação ao conjunto de dados que contém a tabela de destino. A tabela precisa existir antes de você começar a gravar dados nela, a menos que você esteja usando tabelas de modelo. Para mais informações sobre tabelas de modelo, consulte Criação automática de tabelas usando tabelas de modelo.

  2. Verifique a política de cotas para dados de streaming.

  3. Make sure that billing is enabled for your Google Cloud project.

  4. O streaming não está disponível pelo nível gratuito. Se tentar usar o streaming sem ativar o faturamento, este erro será exibido: BigQuery: Streaming insert is not allowed in the free tier.

  5. Atribua papéis do Identity and Access Management (IAM) que concedam aos usuários as permissões necessárias para realizar cada tarefa deste documento.

Permissões necessárias

Para fazer o streaming de dados no BigQuery, você precisa das seguintes permissões do IAM:

  • bigquery.tables.updateData (permite inserir dados na tabela)
  • bigquery.tables.get (permite receber metadados da tabela)
  • bigquery.datasets.get (permite receber metadados do conjunto de dados)
  • bigquery.tables.create (obrigatório se você usar uma tabela de modelo para criar a tabela automaticamente)

Todos estes papéis predefinidos do IAM incluem as permissões necessárias para fazer streaming de dados no BigQuery:

  • roles/bigquery.dataEditor
  • roles/bigquery.dataOwner
  • roles/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.

Fazer streaming de dados para o BigQuery

C#

Antes de testar esta amostra, siga as instruções de configuração do C# no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em C#.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.


using Google.Cloud.BigQuery.V2;

public class BigQueryTableInsertRows
{
    public void TableInsertRows(
        string projectId = "your-project-id",
        string datasetId = "your_dataset_id",
        string tableId = "your_table_id"
    )
    {
        BigQueryClient client = BigQueryClient.Create(projectId);
        BigQueryInsertRow[] rows = new BigQueryInsertRow[]
        {
            // The insert ID is optional, but can avoid duplicate data
            // when retrying inserts.
            new BigQueryInsertRow(insertId: "row1") {
                { "name", "Washington" },
                { "post_abbr", "WA" }
            },
            new BigQueryInsertRow(insertId: "row2") {
                { "name", "Colorado" },
                { "post_abbr", "CO" }
            }
        };
        client.InsertRows(datasetId, tableId, rows);
    }
}

Go

Antes de testar esta amostra, siga as instruções de configuração do Go no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Go.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// Item represents a row item.
type Item struct {
	Name string
	Age  int
}

// Save implements the ValueSaver interface.
// This example disables best-effort de-duplication, which allows for higher throughput.
func (i *Item) Save() (map[string]bigquery.Value, string, error) {
	return map[string]bigquery.Value{
		"full_name": i.Name,
		"age":       i.Age,
	}, bigquery.NoDedupeID, nil
}

// insertRows demonstrates inserting data into a table using the streaming insert mechanism.
func insertRows(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %w", err)
	}
	defer client.Close()

	inserter := client.Dataset(datasetID).Table(tableID).Inserter()
	items := []*Item{
		// Item implements the ValueSaver interface.
		{Name: "Phred Phlyntstone", Age: 32},
		{Name: "Wylma Phlyntstone", Age: 29},
	}
	if err := inserter.Put(ctx, items); err != nil {
		return err
	}
	return nil
}

Java

Antes de testar esta amostra, siga as instruções de configuração do Java no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Java.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryError;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.InsertAllRequest;
import com.google.cloud.bigquery.InsertAllResponse;
import com.google.cloud.bigquery.TableId;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// Sample to inserting rows into a table without running a load job.
public class TableInsertRows {

  public static void main(String[] args) {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    // Create a row to insert
    Map<String, Object> rowContent = new HashMap<>();
    rowContent.put("booleanField", true);
    rowContent.put("numericField", "3.14");
    // TODO(developer): Replace the row id with a unique value for each row.
    String rowId = "ROW_ID";
    tableInsertRows(datasetName, tableName, rowId, rowContent);
  }

  public static void tableInsertRows(
      String datasetName, String tableName, String rowId, Map<String, Object> rowContent) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      // Get table
      TableId tableId = TableId.of(datasetName, tableName);

      // Inserts rowContent into datasetName:tableId.
      InsertAllResponse response =
          bigquery.insertAll(
              InsertAllRequest.newBuilder(tableId)
                  // More rows can be added in the same RPC by invoking .addRow() on the builder.
                  // You can omit the unique row ids to disable de-duplication.
                  .addRow(rowId, rowContent)
                  .build());

      if (response.hasErrors()) {
        // If any of the insertions failed, this lets you inspect the errors
        for (Map.Entry<Long, List<BigQueryError>> entry : response.getInsertErrors().entrySet()) {
          System.out.println("Response error: \n" + entry.getValue());
        }
      }
      System.out.println("Rows successfully inserted into table");
    } catch (BigQueryException e) {
      System.out.println("Insert operation not performed \n" + e.toString());
    }
  }
}

Node.js

Antes de testar esta amostra, siga as instruções de configuração do Node.js no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Node.js.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

// Import the Google Cloud client library
const {BigQuery} = require('@google-cloud/bigquery');
const bigquery = new BigQuery();

async function insertRowsAsStream() {
  // Inserts the JSON objects into my_dataset:my_table.

  /**
   * TODO(developer): Uncomment the following lines before running the sample.
   */
  // const datasetId = 'my_dataset';
  // const tableId = 'my_table';
  const rows = [
    {name: 'Tom', age: 30},
    {name: 'Jane', age: 32},
  ];

  // Insert data into a table
  await bigquery.dataset(datasetId).table(tableId).insert(rows);
  console.log(`Inserted ${rows.length} rows`);
}

PHP

Antes de testar esta amostra, siga as instruções de configuração do PHP no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em PHP.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

use Google\Cloud\BigQuery\BigQueryClient;

/**
 * Stream data into bigquery
 *
 * @param string $projectId The project Id of your Google Cloud Project.
 * @param string $datasetId The BigQuery dataset ID.
 * @param string $tableId The BigQuery table ID.
 * @param string $data Json encoded data For eg,
 *    $data = json_encode([
 *       "field1" => "value1",
 *       "field2" => "value2",
 *    ]);
 */
function stream_row(
    string $projectId,
    string $datasetId,
    string $tableId,
    string $data
): void {
    // instantiate the bigquery table service
    $bigQuery = new BigQueryClient([
      'projectId' => $projectId,
    ]);
    $dataset = $bigQuery->dataset($datasetId);
    $table = $dataset->table($tableId);

    $data = json_decode($data, true);
    $insertResponse = $table->insertRows([
      ['data' => $data],
      // additional rows can go here
    ]);
    if ($insertResponse->isSuccessful()) {
        print('Data streamed into BigQuery successfully' . PHP_EOL);
    } else {
        foreach ($insertResponse->failedRows() as $row) {
            foreach ($row['errors'] as $error) {
                printf('%s: %s' . PHP_EOL, $error['reason'], $error['message']);
            }
        }
    }
}

Python

Antes de testar esta amostra, siga as instruções de configuração do Python no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Python.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of table to append to.
# table_id = "your-project.your_dataset.your_table"

rows_to_insert = [
    {"full_name": "Phred Phlyntstone", "age": 32},
    {"full_name": "Wylma Phlyntstone", "age": 29},
]

errors = client.insert_rows_json(table_id, rows_to_insert)  # Make an API request.
if errors == []:
    print("New rows have been added.")
else:
    print("Encountered errors while inserting rows: {}".format(errors))

Ruby

Antes de testar esta amostra, siga as instruções de configuração do Ruby no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Ruby.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

require "google/cloud/bigquery"

def table_insert_rows dataset_id = "your_dataset_id", table_id = "your_table_id"
  bigquery = Google::Cloud::Bigquery.new
  dataset  = bigquery.dataset dataset_id
  table    = dataset.table table_id

  row_data = [
    { name: "Alice", value: 5  },
    { name: "Bob",   value: 10 }
  ]
  response = table.insert row_data

  if response.success?
    puts "Inserted rows successfully"
  else
    puts "Failed to insert #{response.error_rows.count} rows"
  end
end

Não é necessário preencher o campo insertID ao inserir linhas. O exemplo a seguir mostra como evitar o envio de um insertID para cada linha durante o streaming.

Java

Antes de testar esta amostra, siga as instruções de configuração do Java no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Java.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryError;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.InsertAllRequest;
import com.google.cloud.bigquery.InsertAllResponse;
import com.google.cloud.bigquery.TableId;
import com.google.common.collect.ImmutableList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// Sample to insert rows without row ids in a table
public class TableInsertRowsWithoutRowIds {

  public static void main(String[] args) {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    tableInsertRowsWithoutRowIds(datasetName, tableName);
  }

  public static void tableInsertRowsWithoutRowIds(String datasetName, String tableName) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      final BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();
      // Create rows to insert
      Map<String, Object> rowContent1 = new HashMap<>();
      rowContent1.put("stringField", "Phred Phlyntstone");
      rowContent1.put("numericField", 32);
      Map<String, Object> rowContent2 = new HashMap<>();
      rowContent2.put("stringField", "Wylma Phlyntstone");
      rowContent2.put("numericField", 29);
      InsertAllResponse response =
          bigquery.insertAll(
              InsertAllRequest.newBuilder(TableId.of(datasetName, tableName))
                  // No row ids disable de-duplication, and also disable the retries in the Java
                  // library.
                  .setRows(
                      ImmutableList.of(
                          InsertAllRequest.RowToInsert.of(rowContent1),
                          InsertAllRequest.RowToInsert.of(rowContent2)))
                  .build());

      if (response.hasErrors()) {
        // If any of the insertions failed, this lets you inspect the errors
        for (Map.Entry<Long, List<BigQueryError>> entry : response.getInsertErrors().entrySet()) {
          System.out.println("Response error: \n" + entry.getValue());
        }
      }
      System.out.println("Rows successfully inserted into table without row ids");
    } catch (BigQueryException e) {
      System.out.println("Insert operation not performed \n" + e.toString());
    }
  }
}

Python

Antes de testar esta amostra, siga as instruções de configuração do Python no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Python.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of table to append to.
# table_id = "your-project.your_dataset.your_table"

rows_to_insert = [
    {"full_name": "Phred Phlyntstone", "age": 32},
    {"full_name": "Wylma Phlyntstone", "age": 29},
]

errors = client.insert_rows_json(
    table_id, rows_to_insert, row_ids=[None] * len(rows_to_insert)
)  # Make an API request.
if errors == []:
    print("New rows have been added.")
else:
    print("Encountered errors while inserting rows: {}".format(errors))

Enviar dados de data e hora

Para campos de data e hora, formate os dados no método tabledata.insertAll desta maneira:

Tipo Formato
DATE Uma string no formato "YYYY-MM-DD"
DATETIME Uma string no formato "YYYY-MM-DD [HH:MM:SS]"
TIME Uma string no formato "HH:MM:SS"
TIMESTAMP O número de segundos desde 1970-01-01 (a época do Unix) ou uma string no formato "YYYY-MM-DD HH:MM[:SS]"

Enviar dados do intervalo

Para campos com o tipo RANGE<T>, formate os dados no método tabledata.insertAll como um objeto JSON com dois campos, start e end. Os valores ausentes ou NULL para os campos start e end representam limites ilimitados. Esses campos precisam ter o mesmo formato JSON compatível do tipo T, em que T pode ser DATE, DATETIME ou TIMESTAMP.

No exemplo a seguir, o campo f_range_date representa uma coluna RANGE<DATE> em uma tabela. Uma linha é inserida nessa coluna usando a API tabledata.insertAll.

{
    "f_range_date": {
        "start": "1970-01-02",
        "end": null
    }
}

Disponibilidade de dados de streaming

Os dados ficam disponíveis para análise em tempo real por consultas do GoogleSQL imediatamente depois que o BigQuery reconhece uma solicitação tabledata.insertAll.

As linhas transmitidas recentemente para uma tabela particionada por tempo de processamento têm temporariamente um valor NULL para a pseudocoluna _PARTITIONTIME. Para essas linhas, o BigQuery atribui o valor final NULL da coluna PARTITIONTIME em segundo plano, normalmente em alguns minutos. Em casos raros, isso pode levar até 90 minutos.

Algumas linhas transmitidas recentemente podem não estar disponíveis para cópia da tabela normalmente por alguns minutos. Em casos raros, isso pode levar até 90 minutos. Para ver se os dados estão disponíveis para cópia, verifique se, na resposta tables.get, existe uma seção denominada streamingBuffer. Se a seção streamingBuffer não estiver presente, seus dados estarão disponíveis para cópia. Também é possível usar o campo streamingBuffer.oldestEntryTime para identificar a idade dos registros no buffer de streaming.

Eliminação de duplicação por melhor esforço

Quando você fornece insertId para uma linha inserida, o BigQuery usa esse ID para aceitar a eliminação de duplicação por melhor esforço por até um minuto. Ou seja, se você transmitir a mesma linha com o mesmo insertId mais de uma vez nesse período na mesma tabela, o BigQuery pode eliminar a duplicação de várias ocorrências dessa linha, mantendo apenas uma dessas ocorrências.

O sistema espera que as linhas fornecidas com insertIds idênticos também sejam idênticas. Se duas linhas tiverem insertIds idênticos, o BigQuery vai preservar uma delas por um processo não determinístico.

A eliminação da duplicação costuma se destinar a cenários de nova tentativa em um sistema distribuído em que não é possível determinar o estado de uma inserção por streaming sob determinadas condições de erro. Esses erros podem ser de rede entre o sistema e o BigQuery ou internos do BigQuery. Se você tentar novamente uma inserção, use o mesmo insertId para o mesmo conjunto de linhas e o BigQuery tentará eliminar a duplicação dos seus dados. Para mais informações, consulte solução de problemas com inserções de streaming.

A eliminação de duplicação feita pelo BigQuery é o melhor esforço possível e não pode ser considerado um mecanismo para garantir a ausência de cópias nos dados. Além disso, o BigQuery pode prejudicar a qualidade da eliminação de duplicação por melhor esforço a qualquer momento para garantir maior confiabilidade e disponibilidade dos dados.

Se você tem requisitos rígidos de eliminação de duplicação para os dados, o Google Cloud Datastore é um serviço alternativo compatível com transações.

Como desativar a eliminação de duplicação por melhor esforço

É possível desativar a eliminação de duplicação por melhor esforço ao não preencher o campo insertId de cada linha inserida. Essa é a maneira recomendada de inserir dados.

Apache Beam e Dataflow

Para desativar a eliminação de duplicação por melhor esforço ao usar o conector de E/S do BigQuery do Apache Beam para Java, use o método ignoreInsertIds().

Remoção manual de duplicatas

Para garantir que não existam linhas duplicadas depois do término do streaming, use o seguinte processo manual:

  1. Adicione insertId como uma coluna no esquema da tabela e inclua o valor insertId nos dados de cada linha.
  2. Após o streaming ser interrompido, execute a seguinte consulta para verificar se há duplicatas:

    #standardSQL
    SELECT
      MAX(count) FROM(
      SELECT
        ID_COLUMN,
        count(*) as count
      FROM
        `TABLE_NAME`
      GROUP BY
        ID_COLUMN)

    Se o resultado for maior que 1, existem duplicatas.
  3. Para remover as duplicatas, execute a seguinte consulta. Especifique uma tabela de destino, permita resultados grandes e desative o achatamento de resultados.

    #standardSQL
    SELECT
      * EXCEPT(row_number)
    FROM (
      SELECT
        *,
        ROW_NUMBER()
              OVER (PARTITION BY ID_COLUMN) row_number
      FROM
        `TABLE_NAME`)
    WHERE
      row_number = 1

Observações sobre a consulta de remoção de duplicatas:

  • A estratégia mais segura para a consulta de remoção de duplicatas é direcionar para uma nova tabela. Outra opção é direcionar para a tabela de origem com a disposição de gravação WRITE_TRUNCATE.
  • A consulta de remoção de duplicatas adiciona uma coluna row_number, com o valor 1, ao final do esquema da tabela. A consulta usa uma instrução SELECT * EXCEPT do GoogleSQL para excluir a coluna row_number da tabela de destino. O prefixo #standardSQL ativa o GoogleSQL para essa consulta. Outra opção é selecionar por nomes de coluna específicos para omitir essa coluna.
  • Para consultar dados ativos com duplicatas removidas, também é possível criar uma visualização sobre a tabela usando a consulta de remoção de duplicatas. Lembre-se de que os custos de consulta com a visualização serão calculados com base nas colunas selecionadas. Isso pode resultar em grandes tamanhos verificados de bytes.

Fazer streaming em tabelas particionadas por tempo

Quando você faz stream de dados para uma tabela particionada por tempo, cada partição tem um buffer de streaming. O buffer de streaming é mantido quando você executa um job de carregamento, consulta ou cópia que substitui uma partição ao definir a propriedade writeDisposition como WRITE_TRUNCATE. Se quiser remover o buffer de streaming, verifique se ele está vazio chamando tables.get na partição.

Particionamento por tempo de processamento

Ao fazer streaming para uma tabela particionada por tempo de processamento, o BigQuery infere a partição de destino a partir da hora UTC atual.

Os dados recém-recebidos são colocados temporariamente na partição __UNPARTITIONED__ enquanto estão no buffer de streaming. Quando há dados não particionados suficientes, o BigQuery particiona os dados na partição correta. No entanto, não há SLA sobre o prazo necessário para os dados serem movidos da partição __UNPARTITIONED__. Uma consulta pode excluir dados do buffer de streaming de uma consulta, filtrando os valores NULL da partição __UNPARTITIONED__ usando uma das pseudocolunas ([_PARTITIONTIME] ou [_PARTITIONDATE], dependendo do tipo de dados preferido).

Se estiver fazendo streaming de dados para uma tabela particionada diária, é possível substituir a inferência de data fornecendo um decorador de partição como parte da solicitação insertAll. Inclua o decorador no parâmetro tableId. Por exemplo, é possível fazer o streaming para a partição correspondente a 01-03-2021 para a tabela table1 usando o decorador de partições:

table1$20210301

É possível fazer streaming usando um decorador de partições para partições dos últimos 31 dias e dos próximos 16 dias em relação à data atual, com base na hora UTC atual. Para gravar em partições de datas fora desses limites permitidos, use um job de carregamento ou de consulta, como descrito em Como anexar e substituir dados da tabela particionada.

O streaming usando um decorador de partição só é compatível com tabelas particionadas por dia. Ele não é compatível com tabelas particionadas por hora, mês ou ano.

Para testar, use o comando da CLI bq insert da ferramenta de linha de comando bq. Por exemplo, o comando a seguir transmite de uma única linha para uma partição de 1º de janeiro de 2017 ($20170101) em uma tabela particionada denominada mydataset.mytable:

echo '{"a":1, "b":2}' | bq insert 'mydataset.mytable$20170101'

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__. Quando há dados não particionados suficientes, o BigQuery os particiona automaticamente, colocando-os na partição apropriada. No entanto, não há SLA sobre o prazo necessário para os dados serem movidos da partição __UNPARTITIONED__.

  • Observação: partições diárias são processadas de maneira diferente das partições por hora, mensais e anuais. Somente os dados fora do período (últimos sete dias a três dias futuros) são extraídos para a partição NÃO PARTICIONADA, aguardando para serem particionados novamente. Por outro lado, para tabela particionada por hora, os dados são sempre extraídos para a partição NÃO PARTICIONADA e, posteriormente, reparticionadas.

Criar tabelas automaticamente usando tabelas de modelo

As tabelas de modelo fornecem um mecanismo para dividir uma tabela lógica em muitas tabelas menores para criar conjuntos menores de dados (por exemplo, por ID do usuário). As tabelas de modelo têm várias limitações, conforme descrito abaixo. Já as tabelas particionadas e em cluster são as maneiras recomendadas de conseguir esse comportamento.

Para usar uma tabela de modelo com a API BigQuery, adicione um parâmetro templateSuffix à solicitação insertAll. Na ferramenta de linha de comando bq, adicione a sinalização template_suffix ao comando insert. Se o BigQuery detectar um parâmetro templateSuffix ou a sinalização template_suffix, ele tratará a tabela visada como um modelo base. Ela cria uma nova tabela que compartilha o mesmo esquema da tabela visada e tem um nome que inclui o sufixo especificado:

<targeted_table_name> + <templateSuffix>

Ao usar uma tabela de modelo, você evita a sobrecarga de criar cada tabela individualmente e especificar o esquema para cada tabela. Você só precisa criar um modelo único e fornecer sufixos diferentes para que o BigQuery possa criar as tabelas novas para você. O BigQuery coloca as tabelas no mesmo projeto e conjunto de dados.

As tabelas criadas com as tabelas de modelo geralmente ficam disponíveis em alguns segundos. Em raras ocasiões, elas podem levar mais tempo para ficarem disponíveis.

Alterar o esquema da tabela de modelo

Se você alterar um esquema de tabelas de modelo, todas as tabelas geradas posteriormente usarão o esquema atualizado. As tabelas geradas anteriormente não são afetadas, a menos que a tabela ainda tenha um buffer de streaming.

Se você modificar o esquema de tabelas de modelo de uma maneira compatível com versões anteriores para tabelas que ainda têm um buffer de streaming, o esquema dessas tabelas geradas com streaming ativo também será atualizado. No entanto, se você modificar o esquema de tabelas de modelo de uma maneira não compatível com versões anteriores, todos os dados armazenados em buffer que usam o esquema antigo serão perdidos. Além disso, não é possível fazer streaming de novos dados para as tabelas geradas que usam o esquema antigo, mas agora incompatível.

Depois de alterar um esquema de tabelas de modelo, aguarde até que as alterações se propaguem antes de tentar inserir novos dados ou consultar tabelas geradas. As solicitações para inserir novos campos devem ficar prontas em poucos minutos. As tentativas de consulta dos novos campos podem exigir uma espera mais longa, de até 90 minutos.

Caso seja necessário alterar o esquema de uma tabela gerada, não altere o esquema até que o streaming da tabela de modelo seja interrompido e a seção de estatísticas do streaming da tabela esteja ausente da resposta tables.get(), indicando que nenhum dado está armazenado em buffer na tabela.

As tabelas particionadas e em cluster não apresentam as limitações acima e são o mecanismo recomendado.

Detalhes da tabela de modelo

Valor do sufixo do modelo
O valor templateSuffix (ou --template_suffix) precisa conter apenas letras (a-z, A-Z), números (0-9) ou sublinhados (_). O tamanho máximo combinado do nome e do sufixo da tabela é de 1.024 caracteres.
Cota

As tabelas de modelo estão sujeitas a limitações de cota de streaming. Seu projeto pode criar até 10 tabelas por segundo com tabelas de modelos, semelhante à API tables.insert. Essa cota se aplica somente às tabelas que estão sendo criadas, não às tabelas que estão sendo modificadas.

Se o aplicativo precisar criar mais de 10 tabelas por segundo, recomendamos o uso de tabelas em cluster. Por exemplo, é possível colocar o ID da tabela de alta cardinalidade na coluna-chave de uma única tabela de clustering.

Tempo para ficar ativa

A tabela gerada herda o tempo de expiração do conjunto de dados. Como acontece com os dados de streaming normais, as tabelas geradas não podem ser copiadas imediatamente.

Desduplicação

A desduplicação só acontece entre referências uniformes a uma tabela de destino. Por exemplo, ao fazer streaming simultâneo para uma tabela gerada usando tabelas de modelo e um comando insertAll normal, nenhuma eliminação de duplicação ocorrerá entre linhas inseridas por tabelas de modelo e um comando insertAll normal.

Visualizações

A tabela de modelo e as tabelas geradas não devem ser visualizadas.

Resolver problemas de inserções por streaming

As seções a seguir explicam como resolver erros que ocorrem quando você faz streaming de dados para o BigQuery usando a API de streaming legada. Para mais informações sobre como resolver erros de cota para inserções por streaming, consulte Erros de cota de inserção por streaming.

Códigos de resposta HTTP de falha

Se você receber um código de resposta HTTP de falha, como um erro de rede, não será possível saber se a inserção de streaming foi bem-sucedida. Se você tentar reenviar a solicitação, você acabará com linhas duplicadas na tabela. Para ajudar a proteger a tabela contra duplicação, defina a property insertId ao enviar a solicitação. O BigQuery usa a property insertId para remoção da duplicação.

Se você receber um erro de permissão, de nome de tabela inválido ou de cota excedida, nenhuma linha será inserida, e a solicitação inteira falhará.

Códigos de resposta HTTP de sucesso

Mesmo que você receba um código de resposta HTTP de sucesso, será necessário verificar a property insertErrors da resposta para determinar se as inserções de linha foram bem-sucedidas, porque é possível que o BigQuery tenha conseguido inserir as linhas apenas parcialmente. Você pode encontrar um dos seguintes cenários:

  • Todas as linhas foram inseridas com sucesso. Se a property insertErrors for uma lista vazia, todas as linhas foram inseridas com sucesso.
  • Algumas linhas foram inseridas com sucesso. Exceto nos casos em que houver uma incompatibilidade de esquema em qualquer uma das linhas, as linhas indicadas na propriedade insertErrors não serão inseridas e todas as outras linhas serão inseridas com sucesso. A property errors contém informações detalhadas sobre o motivo de falha de cada linha malsucedida. A property index indica o índice de linha com base em 0 da solicitação à qual o erro se aplica.
  • Nenhuma das linhas foi inserida com sucesso. Se o BigQuery encontrar uma incompatibilidade de esquema em linhas individuais na solicitação, nenhuma das linhas será inserida e uma entrada insertErrors será retornada para cada linha, mesmo as linhas que não tiveram incompatibilidade de esquema. As linhas que não tiveram incompatibilidade de esquema terão um erro com a propriedade reason definida como stopped e podem ser reenviadas como estão. As linhas que falharam exibem informações detalhadas sobre a incompatibilidade de esquema. Para saber mais sobre os tipos de buffer de protocolo compatíveis com cada tipo de dado do BigQuery, consulte Conversões de tipo de dado.

Erros de metadados para inserções de streaming

Como a API de streaming do BigQuery foi projetada para altas taxas de inserção, as modificações na exibição de metadados da tabela subjacente têm consistência eventual durante a interação com o sistema de streaming. Na maioria das vezes, as alterações de metadados são propagadas em minutos. No entanto, durante esse período, as respostas da API podem refletir o estado inconsistente da tabela.

Alguns cenários incluem:

  • Mudanças no esquema. A modificação do esquema de uma tabela que recebeu inserções de streaming recentemente pode causar respostas com erros de incompatibilidade de esquema, porque o sistema de streaming pode não captar imediatamente a alteração de esquema.
  • Criação/exclusão de tabela. Fazer streaming para uma tabela que não existe retorna uma variação de uma resposta notFound. Uma tabela criada em resposta pode não ser reconhecida imediatamente pelas inserções de streaming subsequentes. Da mesma forma, excluir ou recriar uma tabela pode criar um período em que as inserções de streaming são entregues efetivamente à tabela antiga. As inserções de streaming podem não estar presentes na nova tabela.
  • Truncamento da tabela. Truncar os dados de uma tabela (por meio de um job de consulta que usa writeDisposition de WRITE_TRUNCATE) pode causar a eliminação de inserções subsequentes durante o período de consistência.

Dados ausentes/não disponíveis

As inserções por streaming ficam temporariamente no armazenamento otimizado para gravação, que tem características de disponibilidade diferentes do armazenamento gerenciado. Algumas operações no BigQuery não interagem com o armazenamento otimizado para gravação, como jobs de cópia de tabela e métodos de API, como tabledata.list. Os dados de streaming recentes não estarão presentes na tabela de destino ou na saída.