Prácticas recomendadas de la API Storage Write de BigQuery
En este documento se describen las prácticas recomendadas para usar la API Storage Write de BigQuery. Antes de leer este documento, consulte la información general sobre la API Storage Write de BigQuery.
Limitar la frecuencia de creación de emisiones
Antes de crear un flujo, plantéate si puedes usar el flujo predeterminado. En los casos de streaming, el flujo predeterminado tiene menos limitaciones de cuota y se puede adaptar mejor que los flujos creados por la aplicación. Si usas un flujo creado por una aplicación, asegúrate de utilizar el rendimiento máximo en cada flujo antes de crear flujos adicionales. Por ejemplo, usa escrituras asíncronas.
En el caso de los flujos creados por la aplicación, evita llamar a CreateWriteStream
con una frecuencia alta. Por lo general, si superas las 40-50 llamadas por segundo, la latencia de las llamadas a la API aumenta considerablemente (más de 25 segundos). Asegúrate de que tu aplicación pueda aceptar un inicio en frío y aumentar el número de flujos gradualmente, así como limitar la frecuencia de las llamadas CreateWriteStream
. También puedes definir un plazo mayor para esperar a que se complete la llamada, de forma que no falle con un error DeadlineExceeded
. También hay una cuota a largo plazo sobre la tasa máxima de llamadas CreateWriteStream
. Crear emisiones es un proceso que requiere muchos recursos, por lo que la mejor forma de no superar este límite es reducir la frecuencia de creación de emisiones y utilizar al máximo las emisiones que ya tengas.
Gestión de grupos de conexiones
El método AppendRows
crea una conexión bidireccional a un flujo. Puedes abrir varias conexiones en el flujo predeterminado, pero solo una conexión activa en los flujos creados por la aplicación.
Cuando se usa el flujo predeterminado, se puede usar la multiplexación de la API Storage Write para escribir en varias tablas de destino con conexiones compartidas. El multiplexado agrupa las conexiones para mejorar el rendimiento y la utilización de los recursos. Si tu flujo de trabajo tiene más de 20 conexiones simultáneas, te recomendamos que uses la multiplexación. El multiplexado está disponible en Java y Go. Para obtener más información sobre la implementación en Java, consulta Usar multiplexado. Para obtener información sobre la implementación en Go, consulta Compartir conexiones (multiplexación). Si usas el conector de Beam con semántica de al menos una vez, puedes habilitar la multiplexación mediante UseStorageApiConnectionPool. El conector de Spark de Dataproc tiene Multiplexing habilitado de forma predeterminada.
Para obtener el mejor rendimiento, usa una conexión para tantas escrituras de datos como sea posible. No uses una conexión para una sola escritura ni abras y cierres flujos para muchas escrituras pequeñas.
Hay una cuota para el número de conexiones simultáneas que se pueden abrir al mismo tiempo por proyecto. Si se supera el límite, las llamadas a AppendRows
fallarán.
Sin embargo, la cuota de conexiones simultáneas se puede aumentar y, por lo general, no debería ser un factor limitante para el escalado.
Cada llamada a AppendRows
crea un objeto de escritura de datos. Por lo tanto, al usar una secuencia creada por una aplicación, el número de conexiones corresponde al número de secuencias que se han creado. Por lo general, una sola conexión admite un rendimiento de al menos 1 MBps. 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, pero puede superar los 10 MBps.
También hay una cuota para el rendimiento total por proyecto. Representa los bytes por segundo de todas las conexiones que fluyen a través del servicio de la API Storage Write. Si tu proyecto supera esta cuota, puedes solicitar un ajuste de la cuota. Normalmente, esto implica aumentar las cuotas asociadas, como la cuota de conexiones simultáneas, en la misma proporción.
Gestionar los desplazamientos de la secuencia para conseguir una semántica de exactamente una vez
La API Storage Write solo permite escribir en el final actual del flujo, que se mueve a medida que se añaden datos. La posición actual en el flujo se especifica como un desplazamiento desde el inicio del flujo.
Cuando escribes en un flujo creado por una aplicación, puedes especificar el desplazamiento del flujo para conseguir una semántica de escritura exactamente una vez.
Cuando especificas un desplazamiento, la operación de escritura es idempotente, por lo que se puede reintentar sin problemas si se producen errores de red o el servidor no responde. Gestiona los siguientes errores relacionados con los desplazamientos:
ALREADY_EXISTS
(StorageErrorCode.OFFSET_ALREADY_EXISTS
): la fila ya se había escrito. Puedes ignorar este error.OUT_OF_RANGE
(StorageErrorCode.OFFSET_OUT_OF_RANGE
): no se ha podido realizar una operación de escritura anterior. Reintenta la operación desde la última escritura correcta.
Ten en cuenta que estos errores también pueden producirse si defines un valor de desplazamiento incorrecto, por lo que debes gestionar los desplazamientos con cuidado.
Antes de usar los desfases de la secuencia, plantéate si necesitas una semántica de exactamente una vez. Por ejemplo, si tu flujo de procesamiento de datos upstream solo garantiza escrituras al menos una vez o si puedes detectar fácilmente duplicados después de la ingestión de datos, es posible que no necesites escrituras exactamente una vez. En ese caso, te recomendamos que utilices el flujo predeterminado, que no requiere hacer un seguimiento de los desplazamientos de las filas.
No bloquear llamadas de AppendRows
El método AppendRows
es asíncrono. Puedes enviar una serie de escrituras sin bloquear una respuesta para cada escritura individualmente. Los mensajes de respuesta de la conexión bidireccional llegan en el mismo orden en que se pusieron en cola las solicitudes.
Para obtener el mayor rendimiento, llama a AppendRows
sin bloquear para esperar la respuesta.
Gestionar actualizaciones de esquemas
En los casos de transmisión de datos, los esquemas de tabla suelen gestionarse fuera de la canalización de transmisión. Es habitual que el esquema evolucione con el tiempo, por ejemplo, añadiendo nuevos campos que admitan valores nulos. Una canalización sólida debe gestionar las actualizaciones de esquemas fuera de banda.
La API Storage Write admite esquemas de tabla de la siguiente manera:
- La primera solicitud de escritura incluye el esquema.
- Cada fila de datos se envía como un búfer de protocolo binario. BigQuery asigna los datos al esquema.
- Puedes omitir los campos que acepten valores nulos, pero no puedes incluir campos que no estén presentes en el esquema actual. Si envías filas con campos adicionales, la API Storage Write devuelve un
StorageError
conStorageErrorCode.SCHEMA_MISMATCH_EXTRA_FIELD
.
Si quiere enviar campos nuevos en la carga útil, primero debe actualizar el esquema de la tabla en BigQuery. La API Storage Write detecta los cambios de esquema al cabo de poco tiempo, del orden de minutos. Cuando la API Storage Write detecta el cambio de esquema, el mensaje de respuesta AppendRowsResponse
contiene un objeto TableSchema
que describe el nuevo esquema.
Para enviar datos con el esquema actualizado, debes cerrar las conexiones que tengas y abrir otras con el nuevo esquema.
Cliente Java. La biblioteca de cliente de Java proporciona algunas funciones adicionales para las actualizaciones de esquemas a través de la clase JsonStreamWriter
. Después de
una actualización del esquema, JsonStreamWriter
se vuelve a conectar automáticamente con el
esquema actualizado. No es necesario que cierres y vuelvas a abrir la conexión de forma explícita.
Para comprobar los cambios de esquema de forma programática, llama a AppendRowsResponse.hasUpdatedSchema
después de que se complete el método append
.
También puedes configurar el JsonStreamWriter
para que ignore los campos desconocidos en los datos de entrada. Para definir este comportamiento, llama a setIgnoreUnknownFields
. Este comportamiento es similar a la opción ignoreUnknownValues
al usar la API antigua tabledata.insertAll
. Sin embargo, puede provocar una pérdida de datos involuntaria, ya que los campos desconocidos se eliminan de forma silenciosa.