스트림을 만들기 전에 기본 스트림을 사용할 수 있는지 고려하세요. 스트리밍 시나리오의 경우 기본 스트림에는 할당량 제한이 적으며 애플리케이션에서 만든 스트림을 사용하는 것보다 더 잘 확장할 수 있습니다. 애플리케이션에서 만든 스트림을 사용하는 경우 추가 스트림을 만들기 전에 각 스트림에서는 최대 처리량을 활용해야 합니다. 예를 들어 비동기 쓰기를 사용합니다.
애플리케이션에서 만든 스트림의 경우 높은 빈도로 CreateWriteStream을 호출하지 마세요. 일반적으로 초당 호출 수가 40~50개를 초과하면 API 호출의 지연 시간이 크게 늘어납니다(25초 초과). 애플리케이션이 콜드 스타트를 수락하고 스트림 수를 점진적으로 늘릴 수 있는지 확인하고 CreateWriteStream 호출 속도를 제한합니다. DeadlineExceeded 오류와 함께 실패하지 않도록 호출이 완료될 때까지 더 큰 기한을 설정할 수도 있습니다. 또한 CreateWriteStream 호출의 최대 비율에 대한 장기 할당량도 있습니다. 스트림을 만드는 것은 리소스 집약적인 프로세스이므로 스트림 생성 속도를 줄이고 기존 스트림을 최대한 활용하는 것이 이 한도를 초과하지 않는 가장 좋은 방법입니다.
연결 풀 관리
AppendRows 메서드는 스트림에 대한 양방향 연결을 만듭니다. 기본 스트림에서는 여러 연결을 열 수 있지만 애플리케이션에서 생성된 스트림에서는 단일 활성 연결만 열 수 있습니다.
기본 스트림을 사용하는 경우 Storage Write API 다중화를 통해 공유 연결을 사용하여 여러 대상 테이블에 쓸 수 있습니다.
리소스 처리량 및 사용률을 높이기 위한 풀 연결 다중화입니다. 워크플로에 동시 연결이 20개를 초과하는 경우 다중화를 사용하는 것이 좋습니다. 자바 및 Go에서 다중화를 사용할 수 있습니다. 자바 구현 세부정보는 다중화 사용을 참조하세요. Go 구현 세부정보는 연결 공유(다중화)를 참조하세요. 1회 이상 실행되는 시맨틱스가 있는 Beam 커넥터를 사용하는 경우 UseStorageApiConnectionPool을 통해 다중화를 사용 설정할 수 있습니다. Dataproc Spark 커넥터는 기본적으로 다중화가 사용 설정되어 있습니다.
최상의 성능을 위해 하나의 연결에 가능한 한 많은 데이터 쓰기를 사용하세요.
한 번의 쓰기에 하나의 연결을 사용하거나, 여러 개의 작은 쓰기를 위해 스트림을 열고 닫는 것은 좋지 않습니다.
프로젝트당 동시에 열 수 있는 동시 연결 수에 할당량이 있습니다. 한도를 초과하면 AppendRows 호출이 실패합니다.
하지만 동시 연결의 할당량을 늘릴 수 있으며 일반적으로 확장을 제한하는 요소가 되지 않습니다.
AppendRows를 호출할 때마다 새 데이터 작성기 객체가 생성됩니다. 따라서 애플리케이션에서 만든 스트림을 사용하는 경우 연결 수는 생성된 스트림 수에 해당합니다. 일반적으로 단일 연결은 1MBps 이상의 처리량을 지원합니다. 상한값은 네트워크 대역폭, 데이터 스키마, 서버 부하와 같은 여러 요소에 따라 달라지지만 10MBps를 초과할 수 있습니다.
프로젝트당 총 처리량에도 할당량이 있습니다. 이는 Storage Write API 서비스를 통과하는 모든 연결의 초당 바이트를 나타냅니다. 프로젝트가 이 할당량을 초과하면 할당량 조정을 요청할 수 있습니다.
일반적으로 동시 연결 할당량과 같이 그에 수반되는 할당량도 동일한 비율로 늘려야 합니다.
스트림 오프셋을 관리하여 1회만 실행되는 시맨틱스를 달성
Storage Write API는 스트림의 현재 끝에 대한 쓰기만 허용하며, 스트림은 데이터가 추가됨에 따라 이동합니다. 스트림의 현재 위치는 스트림 시작부터 오프셋으로 지정됩니다.
애플리케이션에서 만든 스트림에 쓰는 경우 1회만 실행되는 쓰기 시맨틱스를 갖도록 스트림 오프셋을 지정할 수 있습니다.
오프셋을 지정하면 쓰기 작업이 멱등성을 가지므로 네트워크 오류나 서버의 응답 오류로 인해 재시도할 수 있습니다.
오프셋과 관련된 다음 오류를 처리합니다.
ALREADY_EXISTS(StorageErrorCode.OFFSET_ALREADY_EXISTS): 행이 이미 작성되었습니다. 이 오류는 무시해도 됩니다.
OUT_OF_RANGE(StorageErrorCode.OFFSET_OUT_OF_RANGE): 이전 쓰기 작업이 실패했습니다. 마지막으로 성공한 쓰기에서 다시 시도합니다.
잘못된 오프셋 값을 설정한 경우에도 이러한 오류가 발생할 수 있으므로 오프셋을 신중하게 관리해야 합니다.
스트림 오프셋을 사용하기 전에 1회만 실행되는 시맨틱스가 필요한지 여부를 고려합니다.
예를 들어 업스트림 데이터 파이프라인이 1회 이상 실행되는 쓰기를 보장하거나 데이터 수집 후 중복을 쉽게 감지할 수 있는 경우 1회만 실행되는 쓰기가 필요하지 않을 수도 있습니다. 이 경우에는 행 오프셋을 추적할 필요가 없는 기본 스트림을 사용하는 것이 좋습니다.
AppendRows 호출 차단 안함
AppendRows 메서드는 비동기적입니다. 각 쓰기에 대한 응답을 개별적으로 차단하지 않고 일련의 쓰기를 전송할 수 있습니다. 양방향 연결의 응답 메시지는 요청이 큐에 추가된 것과 동일한 순서로 도착합니다.
최대 처리량을 확보하려면 AppendRows를 차단하지 않고 호출하여 응답을 기다립니다.
스키마 업데이트 처리
데이터 스트리밍 시나리오의 경우 테이블 스키마는 일반적으로 스트리밍 파이프라인 외부에서 관리됩니다. 스키마는 새 null 허용 필드를 추가하는 등 시간이 지남에 따라 발전하는 경우가 많습니다. 강력한 파이프라인이 대역 외 스키마 업데이트를 처리해야 합니다.
Storage Write API는 다음과 같이 테이블 스키마를 지원합니다.
첫 번째 쓰기 요청에 스키마가 포함됩니다.
각 데이터 행을 바이너리 프로토콜 버퍼로 보냅니다. BigQuery는 데이터를 스키마에 매핑합니다.
null 허용 필드는 생략할 수 있지만 현재 스키마에 없는 필드는 포함할 수 없습니다. 추가 필드가 있는 행을 보내면 Storage Write API는 StorageErrorCode.SCHEMA_MISMATCH_EXTRA_FIELD와 함께 StorageError를 반환합니다.
페이로드에서 새 필드를 전송하려면 먼저 BigQuery에서 테이블 스키마를 업데이트해야 합니다. Storage Write API는 잠시 후 몇 분 내에 스키마 변경사항을 감지합니다. Storage Write API가 스키마 변경을 감지하면 AppendRowsResponse 응답 메시지에 새 스키마를 설명하는 TableSchema 객체가 포함됩니다.
업데이트된 스키마를 사용하여 데이터를 전송하려면 기존 연결을 닫고 새 스키마로 새 연결을 열어야 합니다.
자바 클라이언트. 자바 클라이언트 라이브러리는 JsonStreamWriter 클래스를 통해 스키마 업데이트에 대한 몇 가지 추가 기능을 제공합니다. 스키마 업데이트 후 JsonStreamWriter가 업데이트된 스키마에 자동으로 다시 연결됩니다. 연결을 명시적으로 닫았다가 다시 열 필요는 없습니다.
프로그래매틱 방식으로 스키마 변경사항을 확인하려면 append 메서드가 완료된 후 AppendRowsResponse.hasUpdatedSchema를 호출합니다.
입력 데이터에서 알 수 없는 필드를 무시하도록 JsonStreamWriter를 구성할 수도 있습니다. 이 동작을 설정하려면 setIgnoreUnknownFields를 호출하세요. 이 동작은 기존 tabledata.insertAll API를 사용할 때의 ignoreUnknownValues 옵션과 유사합니다. 하지만 알 수 없는 필드가 자동으로 삭제되므로 의도치 않은 데이터 손실이 발생할 수 있습니다.
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["이해하기 어려움","hardToUnderstand","thumb-down"],["잘못된 정보 또는 샘플 코드","incorrectInformationOrSampleCode","thumb-down"],["필요한 정보/샘플이 없음","missingTheInformationSamplesINeed","thumb-down"],["번역 문제","translationIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-09-04(UTC)"],[[["\u003cp\u003eFavor using the default stream for streaming scenarios due to fewer quota limitations and better scalability, and if application-created streams are necessary, maximize throughput on each before creating more.\u003c/p\u003e\n"],["\u003cp\u003eLimit the frequency of \u003ccode\u003eCreateWriteStream\u003c/code\u003e calls to avoid high latency and potential quota issues, especially during application cold starts, and implement a gradual ramp-up for stream creation.\u003c/p\u003e\n"],["\u003cp\u003eUtilize connection multiplexing when dealing with over 20 concurrent connections, particularly when using the default stream, to improve throughput and resource utilization, available in Java and Go, and for the Beam connector.\u003c/p\u003e\n"],["\u003cp\u003eManage stream offsets carefully when using application-created streams to ensure exactly-once write semantics, and be aware of \u003ccode\u003eALREADY_EXISTS\u003c/code\u003e and \u003ccode\u003eOUT_OF_RANGE\u003c/code\u003e errors, but consider if these semantics are necessary.\u003c/p\u003e\n"],["\u003cp\u003eHandle schema updates by first updating the BigQuery table schema, and be aware that the Storage Write API detects schema changes within minutes, then close existing connections and open new ones with the updated schema, or use the Java client's \u003ccode\u003eJsonStreamWriter\u003c/code\u003e for automatic reconnection.\u003c/p\u003e\n"]]],[],null,["# BigQuery Storage Write API best practices\n=========================================\n\nThis document gives best practices for using the BigQuery Storage Write API. Before\nreading this document, read\n[Overview of the BigQuery Storage Write API](/bigquery/docs/write-api#overview).\n\nLimit the rate of stream creation\n---------------------------------\n\nBefore creating a stream, consider whether you can use the\n[default stream](/bigquery/docs/write-api#default_stream). For streaming\nscenarios, the default stream has fewer quota limitations and can scale better\nthan using application-created streams. If you use an application-created\nstream, then make sure to utilize the maximum throughput on each stream before\ncreating additional streams. For example, use\n[asynchronous writes](#do_not_block_on_appendrows_calls).\n\nFor application-created streams, avoid calling `CreateWriteStream` at a high\nfrequency. Generally, if you exceed 40-50 calls per second, the latency of the\nAPI calls grows substantially (\\\u003e25s). Make sure your application can accept a\ncold start and ramp up the number of streams gradually, and limit the rate of\n`CreateWriteStream` calls. You might also set a larger deadline to wait for the\ncall to complete, so that it doesn't fail with a `DeadlineExceeded` error. There\nis also a longer-term [quota](/bigquery/quotas#createwritestream) on the maximum\nrate of `CreateWriteStream` calls. Creating streams is a resource-intensive\nprocess, so reducing the rate of stream creations and fully utilizing existing\nstreams is the best way to not run over this limit.\n\nConnection pool management\n--------------------------\n\nThe `AppendRows` method creates a bidirectional connection to a stream. You can\nopen multiple connections on the default stream, but only a single active\nconnection on application-created streams.\n\nWhen using the default stream, you can use Storage Write API\nmultiplexing to write to multiple destination tables with shared connections.\nMultiplexing pools connections for better throughput and utilization of\nresources. If your workflow has over 20 concurrent connections, we recommend\nthat you use multiplexing. Multiplexing is available in Java\nand Go. For Java implementation details, see\n[Use multiplexing](/bigquery/docs/write-api-streaming#use_multiplexing). For Go\nimplementation details, see\n[Connection Sharing (Multiplexing)](https://pkg.go.dev/cloud.google.com/go/bigquery/storage/managedwriter#hdr-Connection_Sharing__Multiplexing_). If you use the [Beam connector with at-least-once semantics](https://beam.apache.org/documentation/io/built-in/google-bigquery/#at-least-once-semantics),\nyou can enable multiplexing through\n[UseStorageApiConnectionPool](https://beam.apache.org/releases/javadoc/current/org/apache/beam/sdk/io/gcp/bigquery/BigQueryOptions.html#setUseStorageApiConnectionPool-java.lang.Boolean-). Dataproc Spark\nconnector has Multiplexing enabled by default.\n\nFor best performance, use one connection for as many data writes as possible.\nDon't use one connection for just a single write, or open and close streams for\nmany small writes.\n\nThere is a quota on the number of\n[concurrent connections](/bigquery/quotas#concurrent_connections) that can be\nopen at the same time per project. Above the limit, calls to `AppendRows` fail.\nHowever, the quota for concurrent connections can be increased and should not\nnormally be a limiting factor for scaling.\n\nEach call to `AppendRows` creates a new data writer object. So,\nwhen using an application-created stream, the number of connections corresponds\nto the number of streams that have been created. Generally, a single connection\nsupports at least 1MBps of throughput. The upper bound depends on several\nfactors, such as network bandwidth, the schema of the data, and server load, but\ncan exceed 10MBps.\n\nThere is also a quota on the\n[total throughput per project](/bigquery/quotas#writeapi_throughput). This\nrepresents the bytes per second across all connections flowing through the\nStorage Write API service. If your project exceeds this quota, you\ncan [request a quota adjustment](/docs/quotas/help/request_increase).\nTypically this involves raising accompanying quotas, like the concurrent\nconnections quota, in an equal ratio.\n\nManage stream offsets to achieve exactly-once semantics\n-------------------------------------------------------\n\nThe Storage Write API only allows writes to the current end of the\nstream, which moves as data is appended. The current position in the stream is\nspecified as an offset from the start of the stream.\n\nWhen you write to an application-created stream, you can specify the stream\noffset to achieve exactly-once write semantics.\n\nWhen you specify an offset, the write operation is idempotent, which makes it\nsafe to retry due to network errors or unresponsiveness from the server.\nHandle the following errors related to offsets:\n\n- `ALREADY_EXISTS` (`StorageErrorCode.OFFSET_ALREADY_EXISTS`): The row was already written. You can safely ignore this error.\n- `OUT_OF_RANGE` (`StorageErrorCode.OFFSET_OUT_OF_RANGE`): A previous write operation failed. Retry from the last successful write.\n\nNote that these errors can also happen if you set the wrong offset value, so you\nhave to manage offsets carefully.\n\nBefore using stream offsets, consider whether you need exactly-once semantics.\nFor example, if your upstream data pipeline only guarantees at-least-once\nwrites, or if you can easily detect duplicates after data ingestion, then you\nmight not require exactly-once writes. In that case, we recommend using the\ndefault stream, which does not require keeping track of row offsets.\n\nDo not block on `AppendRows` calls\n----------------------------------\n\nThe `AppendRows` method is asynchronous. You can send a series of writes without\nblocking on a response for each write individually. The response messages on the\nbidirectional connection arrive in the same order as the requests were enqueued.\nFor the highest throughput, call `AppendRows` without blocking to wait on the\nresponse.\n\nHandle schema updates\n---------------------\n\nFor data streaming scenarios, table schemas are usually managed outside of the\nstreaming pipeline. It's common for the schema to evolve over time, for example\nby adding new nullable fields. A robust pipeline must handle out-of-band schema\nupdates.\n\nThe Storage Write API supports table schemas as follows:\n\n- The first write request includes the schema.\n- You send each row of data as a binary protocol buffer. BigQuery [maps](/bigquery/docs/write-api#data_type_conversions) the data to the schema.\n- You can omit nullable fields, but you cannot include any fields that are not present in the current schema. If you send rows with extra fields, the Storage Write API returns a [`StorageError`](/bigquery/docs/reference/storage/rpc/google.cloud.bigquery.storage.v1#google.cloud.bigquery.storage.v1.StorageError) with `StorageErrorCode.SCHEMA_MISMATCH_EXTRA_FIELD`.\n\nIf you want to send new fields in the payload, you should first update the table\nschema in BigQuery. The Storage Write API detects\nschema changes after a short time, on the order of minutes. When the\nStorage Write API detects the schema change, the\n[`AppendRowsResponse`](/bigquery/docs/reference/storage/rpc/google.cloud.bigquery.storage.v1#google.cloud.bigquery.storage.v1.AppendRowsResponse) response message contains a\n`TableSchema` object that describes the new schema.\n\nTo send data using the updated schema, you must close existing connections and\nopen new connections with the new schema.\n\n**Java client** . The Java client library provides some additional features for\nschema updates, through the [`JsonStreamWriter`](/java/docs/reference/google-cloud-bigquerystorage/latest/com.google.cloud.bigquery.storage.v1.JsonStreamWriter) class. After\na schema update, the `JsonStreamWriter` automatically reconnects with the\nupdated schema. You don't need to explicitly close and reopen the connection.\nTo check for schema changes programmatically, call\n[`AppendRowsResponse.hasUpdatedSchema`](/java/docs/reference/google-cloud-bigquerystorage/latest/com.google.cloud.bigquery.storage.v1.AppendRowsResponse#com_google_cloud_bigquery_storage_v1_AppendRowsResponse_getUpdatedSchema__) after the `append`\nmethod completes.\n| **Note:** Schema updates aren't immediately visible to the client library, but are detected on the order of minutes.\n\nYou can also configure the `JsonStreamWriter` to ignore unknown fields in the\ninput data. To set this behavior, call\n[`setIgnoreUnknownFields`](/java/docs/reference/google-cloud-bigquerystorage/latest/com.google.cloud.bigquery.storage.v1.JsonStreamWriter.Builder#com_google_cloud_bigquery_storage_v1_JsonStreamWriter_Builder_setIgnoreUnknownFields_boolean_). This behavior is similar to\nthe `ignoreUnknownValues` option when using the legacy\n[`tabledata.insertAll`](/bigquery/docs/reference/rest/v2/tabledata/insertAll)\nAPI. However, it can lead to unintentional data loss, because unknown fields are\nsilently dropped."]]