처리량이 최적화된 쓰기

이 페이지에서는 Spanner에서 쓰기 처리량을 최적화하도록 최대 커밋 (쓰기) 지연 시간을 구성하는 방법을 설명합니다.

개요

데이터 일관성을 보장하기 위해 Spanner는 데이터베이스의 모든 응답 복제본에 쓰기 요청을 전송합니다. 이 복제 프로세스에는 계산 오버헤드가 발생할 수 있습니다. 자세한 내용은 복제를 참조하세요.

처리량 최적화 쓰기를 사용하면 쓰기 그룹을 함께 실행하여 이러한 계산 비용을 분할 상각할 수 있습니다. 이를 위해 Spanner는 약간의 지연 시간을 발생시키고 동일한 응답 참여자에게 전송해야 하는 쓰기 그룹을 수집합니다. 이러한 방식으로 쓰기를 실행하면 지연 시간이 약간 늘어나지만 처리량이 크게 향상될 수 있습니다.

기본 동작

커밋 지연 시간을 설정하지 않은 경우 쓰기 비용을 분할 상각할 것으로 예상되는 경우 Spanner가 약간의 지연을 설정할 수 있습니다.

일반 사용 사례

애플리케이션 요구사항에 따라 쓰기 요청의 지연 시간을 수동으로 설정할 수 있습니다. 또한 최대 커밋 지연 시간을 0ms로 설정하여 지연 시간에 매우 민감한 애플리케이션의 커밋 지연을 중지할 수 있습니다.

지연 시간을 허용하는 애플리케이션이 있고 처리량을 최적화하려는 경우, 커밋 지연 시간을 길게 설정하면 처리량이 크게 향상되는 동시에 각 쓰기에 대한 지연 시간이 길어집니다. 예를 들어 많은 양의 데이터를 일괄 로드할 때 Spanner가 개별 데이터를 얼마나 빨리 쓰는지 신경 쓰지 않는 경우 커밋 지연 시간을 이를테면 약 100ms처럼 더 긴 값으로 설정할 수 있습니다. 우선 100ms로 시작하여 지연 시간과 처리량 절충이 요구를 충족할 때까지 증가 및 축소하는 것을 권장합니다. 대부분의 애플리케이션에서는 20ms에서 100ms 사이의 값이 가장 적합합니다.

지연 시간에 민감한 애플리케이션의 경우 Spanner도 기본적으로 지연 시간에 민감합니다. 워크로드가 급증하는 경우 Spanner에서 약간의 지연 시간을 설정할 수 있습니다. 0ms 값으로 실험해보고 처리량이 늘어나더라도 지연 시간 감소가 애플리케이션에 적합한지 확인할 수 있습니다.

혼합 커밋 지연 시간 설정

쓰기 하위 집합에 대해 서로 다른 최대 커밋 지연 시간을 구성할 수 있습니다. 이렇게 하면 Spanner는 쓰기 집합에 구성된 가장 짧은 지연 시간을 사용합니다. 하지만 더 예측 가능한 동작을 위해서는 대부분의 사용 사례에서 단일 값을 선택하는 것이 좋습니다.

제한사항

커밋 지연 시간을 0~500ms로 설정할 수 있습니다. 커밋 지연 시간을 500ms 이상으로 설정하면 오류가 발생합니다.

커밋 요청의 최대 커밋 지연 설정

최대 커밋 지연 매개변수는 CommitRequest 메서드의 일부입니다. RPC API, REST API, Cloud Spanner 클라이언트 라이브러리를 사용하여 이 메서드에 액세스할 수 있습니다.

Go


import (
	"context"
	"fmt"
	"io"
	"time"

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

// maxCommitDelay sets the maximum commit delay for a transaction.
func maxCommitDelay(w io.Writer, db string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return fmt.Errorf("maxCommitDelay.NewClient: %w", err)
	}
	defer client.Close()

	// Set the maximum commit delay to 100ms.
	// This is the amount of latency this request is willing to incur in order
	// to improve throughput. If this field is not set, Spanner assumes requests
	// are relatively latency sensitive and automatically determines an
	// appropriate delay time. You can specify a batching delay value between 0 and 500 ms.
	// The transaction will also return the commit statistics.
	commitDelay := 100 * time.Millisecond
	resp, err := client.ReadWriteTransactionWithOptions(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
		stmt := spanner.Statement{
			SQL: `INSERT Singers (SingerId, FirstName, LastName)
					VALUES (111, 'Virginia', 'Watson')`,
		}
		rowCount, err := txn.Update(ctx, stmt)
		if err != nil {
			return err
		}
		fmt.Fprintf(w, "%d record(s) inserted.\n", rowCount)
		return nil
	}, spanner.TransactionOptions{CommitOptions: spanner.CommitOptions{MaxCommitDelay: &commitDelay, ReturnCommitStats: true}})
	if err != nil {
		return fmt.Errorf("maxCommitDelay.ReadWriteTransactionWithOptions: %w", err)
	}
	fmt.Fprintf(w, "%d mutations in transaction\n", resp.CommitStats.MutationCount)
	return nil
}

Node.js

// Imports the Google Cloud client library.
const {Spanner, protos} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client.
const spanner = new Spanner({
  projectId: projectId,
});

async function spannerSetMaxCommitDelay() {
  // Gets a reference to a Cloud Spanner instance and database.
  const instance = spanner.instance(instanceId);
  const database = instance.database(databaseId);

  database.runTransaction(async (err, transaction) => {
    if (err) {
      console.error(err);
      return;
    }
    try {
      const [rowCount] = await transaction.runUpdate({
        sql: 'INSERT Singers (SingerId, FirstName, LastName) VALUES (111, @firstName, @lastName)',
        params: {
          firstName: 'Virginia',
          lastName: 'Watson',
        },
      });

      console.log(
        `Successfully inserted ${rowCount} record into the Singers table.`
      );

      await transaction.commit({
        // The maximum amount of time to delay the transaction to improve
        // throughput.
        maxCommitDelay: protos.google.protobuf.Duration({
          seconds: 0, // 0 seconds
          nanos: 100000000, // 100,000,000 nanoseconds = 100 milliseconds
        }),
      });
    } catch (err) {
      console.error('ERROR:', err);
    } finally {
      // Close the database when finished.
      database.close();
    }
  });
}
spannerSetMaxCommitDelay();

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

def insert_singers(transaction):
    row_ct = transaction.execute_update(
        "INSERT Singers (SingerId, FirstName, LastName) "
        " VALUES (111, 'Grace', 'Bennis')"
    )

    print("{} record(s) inserted.".format(row_ct))

database.run_in_transaction(
    insert_singers, max_commit_delay=datetime.timedelta(milliseconds=100)
)

Ruby

require "google/cloud/spanner"

##
# This is a snippet for showcasing how to pass max_commit_delay in  commit_options.
#
# @param project_id  [String] The ID of the Google Cloud project.
# @param instance_id [String] The ID of the spanner instance.
# @param database_id [String] The ID of the database.
#
def spanner_set_max_commit_delay project_id:, instance_id:, database_id:
  # Instantiates a client
  spanner = Google::Cloud::Spanner.new project: project_id
  client  = spanner.client instance_id, database_id

  records = [
    { SingerId: 1, AlbumId: 1, MarketingBudget: 200_000 },
    { SingerId: 2, AlbumId: 2, MarketingBudget: 400_000 }
  ]
  # max_commit_delay is the amount of latency in millisecond, this request
  # is willing to incur in order to improve throughput.
  # The commit delay must be at least 0ms and at most 500ms.
  # Default value is nil.
  commit_options = {
    return_commit_stats: true,
    max_commit_delay: 100
  }
  resp = client.upsert "Albums", records, commit_options: commit_options
  puts "Updated data with #{resp.stats.mutation_count} mutations."
end

쓰기 요청 지연 시간 모니터링

Google Cloud 콘솔을 사용하여 Spanner CPU 사용률과 지연 시간을 모니터링할 수 있습니다. 쓰기 요청에 대해 지연 시간을 길게 설정하면 CPU 사용률이 감소할 수 있지만 지연 시간이 늘어날 것으로 예상됩니다. Spanner 요청의 지연 시간에 대한 자세한 내용은 Spanner API 요청 지연 시간 캡처 및 시각화를 참조하세요.