Introducción a la API de BigQuery Storage Write
La API de BigQuery Storage Write es una API de transferencia de datos unificada para BigQuery. Combina la transferencia de transmisión y la carga por lotes en una sola API de alto rendimiento. Puedes usar la API de Storage Write para transmitir registros a BigQuery en tiempo real o procesar por lotes una gran cantidad arbitraria de registros y confirmarlos en una sola operación atómica.
Ventajas de usar la API de Storage Write
Semántica de entrega exacta una vez. La API de Storage Write admite una semántica de tipo “solo una vez” a través del uso de compensaciones de transmisión. A diferencia del método tabledata.insertAll
, la API de Storage Write nunca escribe dos mensajes que tienen la misma compensación dentro de una transmisión, si el cliente proporciona compensaciones de transmisión cuando agrega registros.
Transacciones a nivel de transmisión. Puedes escribir datos en una transmisión y confirmarlos como una sola transacción. Si la operación de confirmación falla, puedes reintentar la operación de forma segura.
Transacciones en las transmisiones. Varios trabajadores pueden crear sus propias transmisiones para procesar datos de forma independiente. Cuando todos los trabajadores finalizan, puedes confirmar todas las transmisiones como una transacción.
Protocolo eficiente. La API de Storage Write es más eficiente que el método insertAll
anterior porque usa transmisión de gRPC en lugar de REST a través de HTTP. La API de Storage Write también admite formatos de objetos binarios en forma de búferes de protocolo, que son un formato de conexión más eficiente que JSON.
Las solicitudes de escritura son asíncronas con orden garantizado.
Detección de actualización del esquema. Si el esquema de la tabla subyacente cambia mientras el cliente está transmitiendo, la API de Storage Write notifica al cliente. El cliente puede decidir si quiere volver a conectarse a través del esquema actualizado o continuar escribiendo en la conexión existente.
Costo menor. La API de Storage Write tiene un costo mucho menor que la API de transmisión insertAll
anterior. Además, puedes transferir hasta 2 TiB por mes de forma gratuita.
Permisos necesarios
Para usar la API de Storage Write, debes tener los permisos bigquery.tables.updateData
.
Las siguientes funciones predefinidas de Identity and Access Management (IAM) incluyen los permisos bigquery.tables.updateData
:
bigquery.dataEditor
bigquery.dataOwner
bigquery.admin
Para obtener más información sobre las funciones de IAM y los permisos en BigQuery, consulta Funciones y permisos predefinidos.
Permisos de autenticación
El uso de la API de Storage Write requiere uno de los siguientes permisos de OAuth:
https://www.googleapis.com/auth/bigquery
https://www.googleapis.com/auth/cloud-platform
https://www.googleapis.com/auth/bigquery.insertdata
Para obtener más información, consulta Descripción general de la autenticación.
Descripción general de la API de Storage Write
La abstracción principal en la API de Storage Write es una transmisión. Una transmisión escribe datos en una tabla de BigQuery. Más de una transmisión puede escribir de forma simultánea en la misma tabla.
Transmisión predeterminada
La API de Storage Write proporciona una transmisión predeterminada, diseñada para situaciones de transmisión en las que tienes datos que llegan de forma continua. Tiene las siguientes características:
- Los datos escritos en la transmisión predeterminada están disponibles de inmediato para realizar consultas.
- La transmisión predeterminada admite semántica de al menos una vez.
- No necesitas crear de forma explícita la transmisión predeterminada.
Si migras desde la API tabledata.insertall
heredada, considera usar la transmisión predeterminada. Tiene una semántica de escritura similar, con mayor resiliencia de datos y menos restricciones de escalamiento.
Flujo de la API:
AppendRows
(bucle)
Para obtener más información y el código de ejemplo, consulta Usa la transmisión predeterminada para la semántica de al menos una vez.
Transmisiones creadas por la aplicación
Puedes crear una transmisión de forma explícita si necesitas uno de los siguientes comportamientos:
- Escribe semánticas de escritura de tipo “solo una vez” a través del uso de desplazamientos de transmisión.
- Asistencia con propiedades ACID adicionales.
En general, las transmisiones creadas por la aplicación brindan un mayor control sobre la funcionalidad a costa de una complejidad adicional.
Cuando creas una transmisión, especificas un tipo. El tipo controla cuándo los datos escritos en la transmisión se vuelven visibles en BigQuery para su lectura.
Tipo pendiente
En tipo pendiente, los registros se almacenan en búfer en un estado pendiente hasta que confirmes la transmisión. Cuando confirmas una transmisión, todos los datos pendientes pasan a estar disponibles para su lectura. La confirmación es una operación atómica. Usa este tipo para las cargas de trabajo por lotes como alternativa a los trabajos de carga de BigQuery. Para obtener más información, consulta Carga datos por lotes con la API de Storage Write.
Flujo de la API:
Tipo de compromiso
En el tipo de compromiso, los registros están disponibles para su lectura inmediata a medida que los escribes en la transmisión. Usa este tipo para las cargas de trabajo de transmisión que necesitan una latencia mínima de lectura. La transmisión predeterminada usa al menos una forma del tipo confirmado. Para obtener más información, consulta Usa el tipo de confirmación para la semántica de tipo "solo una vez".
Flujo de la API:
CreateWriteStream
AppendRows
(bucle)FinalizeWriteStream
(opcional)
Tipo de almacenamiento en búfer
El tipo de almacenamiento en búfer es un tipo avanzado que, por lo general, no se debe usar con el conector de E/S de BigQuery de Apache Beam. Si tienes lotes pequeños que deseas garantizar que aparezcan juntos, usa el tipo de confirmación y envía cada lote en una solicitud. En este tipo, se proporcionan confirmaciones a nivel de fila, y los registros se almacenan en búfer hasta que las filas se confirmen a través de la limpieza de la transmisión.
Flujo de la API:
CreateWriteStream
AppendRows
⇒FlushRows
(bucle)FinalizeWriteStream
(opcional)
Selecciona un tipo
Usa el siguiente diagrama de flujo para decidir qué tipo es mejor para tu carga de trabajo:
Detalles de la API
Ten en cuenta lo siguiente cuando uses la API de Storage Write:
AppendRows
El método AppendRows
agrega uno o más registros a la transmisión. La primera llamada a AppendRows
debe contener un nombre de transmisión junto con el esquema de datos, especificado como DescriptorProto
. Como práctica recomendada, envía un lote de filas en cada llamada a AppendRows
. No envíes una fila a la vez.
Manejo de búferes de Proto
Los búferes de protocolo proporcionan un mecanismo extensible, con un formato de lenguaje y plataforma neutros, para serializar datos estructurados de manera compatible con versiones anteriores y futuras. Son excelentes porque proporcionan almacenamiento de datos compacto a través de un análisis rápido y eficiente. Para obtener más información sobre los búferes de protocolo, consulta Descripción general del búfer de protocolo.
Si vas a consumir la API de forma directa con un mensaje de búfer de protocolo predefinido, el mensaje de búfer de protocolo no puede usar un especificador package
, y todos los tipos anidados o de enumeración deben definirse en la parte superior nivel superior.
No se permiten referencias a mensajes externos. Para ver un ejemplo, consulta sample_data.proto.
Los clientes Java y Go admiten búferes de protocolo arbitrarios, ya que la biblioteca cliente normaliza el esquema de búfer de protocolo.
FinalizeWriteStream
El método FinalizeWriteStream
finaliza la transmisión para que no se puedan agregar datos nuevos. Este método es obligatorio en el
tipo Pending
y opcional en los tipos
Committed
y
Buffered
. La transmisión predeterminada no es compatible con este método.
Manejo de errores
Si se produce un error, el google.rpc.Status
que se muestra puede incluir un StorageError
en los detalles del error. Revisa
StorageErrorCode
para encontrar el tipo de error específico. Para obtener más información sobre el modelo de error de la API de Google, consulta Errores.
Conexiones
La API de Storage Write es una API de gRPC que usa conexiones bidireccionales. El método AppendRows
crea una conexión a una transmisión. Puedes abrir varias conexiones en la transmisión predeterminada. Estos agregados son asíncronos,
lo que te permite enviar una serie de operaciones de escritura de forma simultánea. Los mensajes de respuesta en cada conexión bidireccional llegan en el mismo orden en que se enviaron las solicitudes.
Las transmisiones creadas por la aplicación solo pueden tener una única conexión activa. Como práctica recomendada, limita la cantidad de conexiones activas y usa una conexión para la mayor cantidad posible de escrituras de datos. Cuando usas la transmisión predeterminada en Java o Go, puedes usar la multiplexación de la API de Storage Write para escribir en varias tablas de destino con conexiones compartidas.
En general, una sola conexión admite al menos 1 Mbps de capacidad de procesamiento. El límite superior depende de varios factores, como el ancho de banda de la red, el esquema de los datos y la carga del servidor. Cuando una conexión alcanza el límite de capacidad de procesamiento, las solicitudes entrantes se pueden rechazar o poner en cola hasta que disminuya la cantidad de solicitudes en tránsito. Si necesitas más capacidad de procesamiento, crea más conexiones.
BigQuery cierra la conexión de gRPC si la conexión permanece inactiva por mucho tiempo. Si esto sucede, el código de respuesta es HTTP 409
. La conexión de gRPC también se puede cerrar en caso de reinicio del servidor o por otros motivos. Si se produce un error de conexión, crea una conexión nueva. Las bibliotecas cliente de Java y Go se vuelven a conectar de forma automática si se cierra la conexión.
Compatibilidad de la biblioteca cliente
Las bibliotecas cliente para la API de Storage Write existen en varios lenguajes de programación y exponen las construcciones subyacentes de la API basada en gRPC. Esta API aprovecha funciones avanzadas como la transmisión bidireccional, lo que puede requerir trabajo de desarrollo adicional para la compatibilidad. En este sentido, hay una serie de abstracciones de nivel superior disponibles para esta API, lo que simplifica esas interacciones y reduce los problemas de los desarrolladores. Recomendamos aprovechar estas otras abstracciones de biblioteca cuando sea posible.
En esta sección, se proporcionan detalles adicionales sobre los lenguajes y las bibliotecas en los que se proporcionaron a los desarrolladores capacidades adicionales más allá de la API generada.
Para ver muestras de código relacionadas con la API de Storage Write, consulta aquí.
Cliente de Java
La biblioteca cliente de Java proporciona dos objetos de escritor:
StreamWriter
: Acepta datos en formato de búfer de protocolo.JsonStreamWriter
: acepta datos en formato JSON y los convierte en búferes de protocolo antes de enviarlos por el cable.JsonStreamWriter
también admite actualizaciones automáticas de esquemas. Si el esquema de la tabla cambia, el escritor se vuelve a conectar de manera automática con el esquema nuevo, lo que permite que el cliente envíe datos con el esquema nuevo.
El modelo de programación es similar para ambos escritores. La diferencia principal es la forma en que le das formato a la carga útil.
El objeto de escritor administra una conexión de la API de Storage Write. El objeto de escritor limpia las solicitudes de forma automática, agrega los encabezados de enrutamiento regionales a las solicitudes y se vuelve a conectar después de los errores de conexión. Si usas la API de gRPC de forma directa, debes manejar estos detalles.
Cliente de Go
El cliente de Go usa una arquitectura cliente-servidor para codificar los mensajes en formato del búfer de protocolo usando proto2. Consulta la documentación de Go para obtener detalles sobre cómo usar el cliente de Go con código de ejemplo.
Cliente de Python
El cliente de Python es un cliente de nivel inferior que une la API de gRPC. Para usar este cliente, debes enviar los datos como búferes de protocolo, como se describe en Flujo de API.
Para obtener más información sobre el uso de búferes de protocolo con Python, lee el instructivo sobre los conceptos básicos del búfer de protocolo en Python.
Cliente de NodeJS
La biblioteca cliente de NodeJS acepta la entrada JSON y proporciona asistencia automática para la reconexión. Consulta la documentación para obtener detalles sobre cómo usar el cliente.
Conversiones de tipos de datos
En la siguiente tabla, se muestran los tipos de búfer de protocolo admitidos para cada tipo de datos de BigQuery:
Tipo de datos de BigQuery | Tipos de búfer de protocolo admitidos |
---|---|
BOOL |
bool , int32 , int64 ,
uint32 , uint64 , google.protobuf.BoolValue |
BYTES |
bytes , string , google.protobuf.BytesValue |
DATE |
int32 (recomendado), int64 El valor es la cantidad de días transcurridos desde el tiempo Unix (1970-01-01). El rango válido es `-719162` (0001-01-01) a `2932896` (9999-12-31). |
DATETIME , TIME |
string
|
int64
Usa la clase
|
|
FLOAT |
double , float , google.protobuf.DoubleValue , google.protobuf.FloatValue |
GEOGRAPHY |
string
El valor es una geometría en 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 Usa la clase
|
|
STRING |
string , enum , google.protobuf.StringValue |
TIME |
string
El valor debe ser un literal de |
TIMESTAMP |
int64 (recomendado), int32 ,
uint32 , google.protobuf.Timestamp
El valor se proporciona en microsegundos desde el tiempo Unix (1970-01-01). |
INTERVAL |
string , google.protobuf.Duration
El valor de cadena debe ser un literal de |
RANGE<T> (vista previa) |
message
Un tipo de mensaje anidado en el proto con dos campos,
|
REPEATED FIELD |
array
Un tipo de array en el proto corresponde a un campo repetido en BigQuery. |
RECORD |
message
Un tipo de mensaje anidado en el proto corresponde a un campo de registro en BigQuery. |
Controla la falta de disponibilidad
Si vuelves a intentarlo con una retirada exponencial puedes mitigar errores aleatorios y períodos breves de falta de disponibilidad del servicio, pero debes estar más pendiente para evitar quitar filas durante la falta de disponibilidad extendida. En particular, si un cliente no puede insertar una fila de forma persistente, ¿qué debe hacer?
La respuesta depende de tus requisitos. Por ejemplo, si BigQuery se usa para estadísticas operativas en las que algunas filas faltantes son aceptables, el cliente puede renunciar después de unos pocos reintentos y descartar los datos. Si, en cambio, cada fila es crucial para la empresa, como con los datos financieros, debes tener una estrategia para conservar los datos hasta que se puedan insertar más adelante.
Una forma común de tratar errores persistentes es publicar las filas en un tema de Pub/Sub para una evaluación posterior y una posible inserción. Otro método común es conservar de forma temporal los datos en el cliente. Ambos métodos pueden mantener a los clientes bloqueados y, al mismo tiempo, garantizar que todas las filas se puedan insertar una vez que se restablezca la disponibilidad.
Partición de columnas por unidad de tiempo
Puedes transmitir datos a una tabla particionada en una columna DATE
, DATETIME
o TIMESTAMP
que esté entre 5 años en el pasado y 1 año en el futuro.
Los datos fuera de este rango se rechazan.
Cuando los datos se transmiten, al principio se colocan en la partición __UNPARTITIONED__
. Una vez que se recopilan suficientes datos no particionados, BigQuery vuelve a particionarlos y los coloca en la partición adecuada.
Sin embargo, no existe un Acuerdo de Nivel de Servicio(ANS) que defina cuánto tiempo les tomará a esos datos salir de la partición __UNPARTITIONED__
.
La API de Storage Write no admite el uso de decoradores de partición.
Métricas de la API de Storage Write
Para que las métricas supervisen la transferencia de datos con la API de Storage Write, como la latencia del nivel de solicitud del servidor, las conexiones simultáneas, los bytes subidos y las filas subidas, consulta Métricas de Google Cloud.
Usa el lenguaje de manipulación de datos (DML) con datos transmitidos de forma reciente
Puedes usar el lenguaje de manipulación de datos (DML), como las declaraciones UPDATE
, DELETE
o MERGE
, para modificar las filas que la API de Storage Write de BigQuery escribió de forma reciente en una tabla de BigQuery. Las operaciones de escritura recientes son aquellas que ocurrieron en los últimos 30 minutos.
Si deseas obtener más información sobre el uso de DML para modificar tus datos transmitidos, consulta Usa el lenguaje de manipulación de datos.
Limitaciones
- La compatibilidad para ejecutar declaraciones DML mutables en datos transmitidos recientemente no se extiende a los datos transmitidos a través de la API de transmisión de insertAll.
- No se admite la ejecución de declaraciones DML mutables dentro de una transacción de varias declaraciones en datos transmitidos de forma reciente.
Cuotas de la API de Storage Write
Para obtener información sobre las cuotas y los límites de la API de Storage Write, consulta Cuotas y límites de la API de BigQuery Storage Write.
Puedes supervisar las conexiones simultáneas y el uso de cuotas de capacidad de procesamiento en la página Cuotas de la consola de Google Cloud.
Calcula la capacidad de procesamiento
Supongamos que tu objetivo es recopilar registros de 100 millones de extremos y crear un registro de 1,500 por minuto. Luego, puedes estimar la capacidad de procesamiento como 100 million * 1,500 / 60 seconds = 2.5 GB per second
.
Debes asegurarte de antemano de tener una cuota adecuada para entregar esta capacidad de procesamiento.
Precios de la API de Storage Write
Para obtener información sobre los precios, consulta Precios de transferencia de datos.
Ejemplo de caso de uso
Supongamos que hay una canalización que procesa datos de eventos de registros de extremos. Los eventos se generan de forma continua y deben estar disponibles para realizar consultas en BigQuery lo antes posible. Dado que la actualidad de los datos es fundamental para este caso de uso, la API de Storage Write es la mejor opción para transferir datos a BigQuery. Una arquitectura recomendada para mantener estos extremos eficientes es enviar eventos a Pub/Sub, desde donde los consume una canalización de Dataflow de transmisión que se transmite de forma directa a BigQuery.
Una preocupación principal de confiabilidad de esta arquitectura es cómo lidiar con el fallo de insertar un registro en BigQuery. Si cada registro es importante y no se puede perder, los datos se deben almacenar en búfer antes de intentar insertarlos. En la arquitectura recomendada anterior, Pub/Sub puede desempeñar la función de un búfer con sus capacidades de retención de mensajes. La canalización de Dataflow debe estar configurada para reintentar las inserciones de transmisión de BigQuery con una retirada exponencial truncada. Después de que se agota la capacidad de Pub/Sub como un búfer, por ejemplo, en el caso de una falta de disponibilidad prolongada de BigQuery o de una falla de la red, los datos se deben conservar en el cliente y el cliente necesita un mecanismo para reanudar la inserción de registros persistentes una vez que se restablece la disponibilidad. Para obtener más información sobre cómo manejar esta situación, consulta la entrada de blog de la Guía de confiabilidad de Google Pub/Sub.
Otro caso de falla que se debe controlar es el de un registro tóxico. Un registro tóxico es un registro que BigQuery rechazó porque no se puede insertar con un error que no se puede reintentar o un registro que no se insertó de forma correcta después de la cantidad máxima de reintentos. Ambos tipos de registros se deben almacenar en una “cola de mensajes no entregados” a través de la canalización de Dataflow para una investigación más detallada.
Si se requiere una semántica de una y solo una vez, crea una transmisión de escritura en tipo confirmado, con compensaciones de registros proporcionadas por el cliente. Esto evita los duplicados, ya que la operación de escritura solo se realiza si el valor de desplazamiento coincide con el desplazamiento de anexo siguiente. Si no se proporciona un desplazamiento, se agregan los registros al extremo actual de la transmisión y, si se reintenta una operación fallida, es posible que el registro aparezca más de una vez en la transmisión.
Si no se requieren garantías de una sola vez, escribir en la transmisión predeterminada permite una capacidad de procesamiento mayor y no se considera en el límite de cuota sobre la creación de transmisiones de escritura.
Estima la capacidad de procesamiento de tu red y asegúrate con anticipación de que tengas una cuota adecuada para entregar la capacidad de procesamiento.
Si tu carga de trabajo genera o procesa datos a una tasa muy desigual, intenta disminuir los picos de carga en el cliente y transmitir a BigQuery con una capacidad de procesamiento constante. Esto puede simplificar la planificación de capacidad. Si eso no es posible, asegúrate de estar preparado para controlar errores 429
(recursos agotados) en caso de que tu capacidad de procesamiento supere la cuota durante aumentos repentinos cortos.
¿Qué sigue?
- Transmite datos con la API de Storage Write
- Carga datos por lotes con la API de Storage Write
- Prácticas recomendadas para la API de Storage Write