트랜잭션의 커밋 통계 검색

Cloud Spanner는 트랜잭션 문제를 보다 효과적으로 파악하고 최적화 및 진단하기 위해 트랜잭션 커밋 통계에 액세스할 수 있도록 합니다. 현재 트랜잭션의 총 변형 수를 검색할 수 있습니다.

커밋 통계를 사용해야 하는 경우

트랜잭션의 변형 수를 알면 다음 시나리오에서 유용할 수 있습니다.

왕복 횟수 최적화

애플리케이션 성능 향상을 위해 각 트랜잭션에서 가능한 한 많은 작업을 수행하여 데이터베이스로의 왕복 횟수를 줄일 수 있습니다. 이 시나리오에서는 트랜잭션당 변형 수를 최대화는 동시에 시스템 한도 내에서 유지하려고 합니다.

한도 미만을 유지하면서 트랜잭션당 커밋할 수 있는 행 수를 확인하려면 먼저 트랜잭션에서 하나의 행을 커밋합니다. 이렇게 하면 행당 변형 수의 기준이 됩니다. 그 다음 시스템 한도를 기준으로 분할하여 트랜잭션당 행 번호를 가져옵니다. 변형이 계산되는 방법에 대한 자세한 내용은 이 참고를 참조하세요.

특히 더 많은 잠금 경합이 발생하는 경우 왕복 횟수 최적화가 항상 유용하지는 않습니다. 잠금 통계를 사용하여 데이터베이스에서 잠금 충돌 문제를 해결할 수 있습니다.

시스템 한도를 넘지 않도록 트랜잭션 모니터링

애플리케이션 사용량이 증가하면 트랜잭션 내 변형 수도 증가할 수 있습니다. 시스템 한도에 도달하지 못하고 트랜잭션이 실패하는 경우를 방지하려면 시간 경과에 따른 변형 수 커밋 통계를 사전에 모니터링하면 됩니다. 동일한 트랜잭션에서 이 값이 증가하는 경우 이전 섹션에서 설명한 대로 트랜잭션을 다시 최적화하는 것이 좋습니다.

커밋 통계에 액세스하는 방법

커밋 통계는 기본적으로 반환되지 않습니다. 대신 각 CommitRequest에서 return_commit_stats 플래그를 true로 설정해야 합니다. 커밋 시도가 트랜잭션에 허용되는 최대 변형 수를 초과하면 커밋이 실패하고 INVALID_ARGUMENT 오류가 반환됩니다.

다음은 Cloud Spanner 클라이언트 라이브러리를 사용하여 커밋 통계를 반환하는 방법의 예입니다.

커밋 통계 검색

다음 샘플은 Cloud Spanner 클라이언트 라이브러리를 사용하여 커밋 통계를 가져오는 방법을 보여줍니다.

C++

다음 코드는 각 행에 2개의 행과 3개의 열을 삽입하거나 업데이트하므로 CommitOptions에서 set_return_stats()를 설정하고 변형 수 6을 반환합니다.

void GetCommitStatistics(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  auto commit = client.Commit(
      spanner::Mutations{
          spanner::UpdateMutationBuilder(
              "Albums", {"SingerId", "AlbumId", "MarketingBudget"})
              .EmplaceRow(1, 1, 200000)
              .EmplaceRow(2, 2, 400000)
              .Build()},
      google::cloud::Options{}.set<spanner::CommitReturnStatsOption>(true));

  if (!commit) throw std::runtime_error(commit.status().message());
  if (commit->commit_stats) {
    std::cout << "Updated data with " << commit->commit_stats->mutation_count
              << " mutations.\n";
  }
  std::cout << "Update was successful [spanner_get_commit_stats]\n";
}

C#

C#에서 커밋 통계는 API를 통해 직접 반환되지 않습니다. 대신 기본 로거가 정보 로그 수준으로 로깅됩니다.

다음 코드는 SpannerConnectionStringBuilderLogCommitStats 속성을 true로 설정하여 모든 트랜잭션에 커밋 통계 로깅을 사용 설정합니다. 또한 이 코드는 마지막으로 표시된 커밋 응답에 대한 참조를 유지하는 샘플 로거를 구현합니다. 그 다음 MutationCount가 이 응답에서 검색되어 표시됩니다.


using Google.Cloud.Spanner.Data;
using Google.Cloud.Spanner.V1;
using Google.Cloud.Spanner.V1.Internal.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;

public class LogCommitStatsAsyncSample
{
    public async Task<long> LogCommitStatsAsync(string projectId, string instanceId, string databaseId)
    {
        // Commit statistics are logged at level Info by the default logger.
        // This sample uses a custom logger to access the commit statistics.
        // See https://googleapis.github.io/google-cloud-dotnet/docs/Google.Cloud.Spanner.Data/logging.html
        // for more information on how to use loggers.
        var logger = new CommitStatsSampleLogger();
        var options = new SessionPoolOptions();
        var poolManager = SessionPoolManager.Create(options, logger);
        var connectionStringBuilder = new SpannerConnectionStringBuilder
        {
            ConnectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}",
            // Set LogCommitStats to true to enable logging commit statistics for all transactions on the connection.
            // LogCommitStats can also be enabled/disabled for individual Spanner transactions.
            LogCommitStats = true,
            SessionPoolManager = poolManager,
        };

        using var connection = new SpannerConnection(connectionStringBuilder);
        await connection.OpenAsync();

        using var cmd = connection.CreateDmlCommand("INSERT Singers (SingerId, FirstName, LastName) VALUES (110, 'Virginia', 'Watson')");
        var rowCount = await cmd.ExecuteNonQueryAsync();
        var mutationCount = logger._lastCommitResponse.CommitStats.MutationCount;

        Console.WriteLine($"{rowCount} row(s) inserted...");
        Console.WriteLine($"{mutationCount} mutation(s) in transaction...");

        return mutationCount;
    }

    /// <summary>
    /// Sample logger that keeps a reference to the last seen commit response.
    /// Use the default logger if you only want to log the commit stats.
    /// </summary>
    public class CommitStatsSampleLogger : Logger
    {
        internal CommitResponse _lastCommitResponse;

        /// <summary>
        /// This method is called when a transaction that requested commit stats is committed.
        /// </summary>
        public override void LogCommitStats(CommitRequest request, CommitResponse response)
        {
            _lastCommitResponse = response;
            base.LogCommitStats(request, response);
        }

        protected override void LogImpl(LogLevel level, string message, Exception exception) =>
            WriteLine(exception == null ? $"{level}: {message}" : $"{level}: {message}, Exception: {exception}");

        protected override void LogPerformanceEntries(IEnumerable<string> entries)
        {
            string separator = Environment.NewLine + "  ";
            WriteLine($"Performance:{separator}{string.Join(separator, entries)}");
        }

        private void WriteLine(string line) => Trace.TraceInformation(line);
    }
}

Go

다음 코드는 ReturnCommitStats 플래그를 설정하고 트랜잭션이 성공적으로 커밋될 때 변형 수를 출력합니다.


import (
	"context"
	"fmt"
	"io"

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

func commitStats(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return fmt.Errorf("commitStats.NewClient: %v", err)
	}
	defer client.Close()

	resp, err := client.ReadWriteTransactionWithOptions(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
		stmt := spanner.Statement{
			SQL: `INSERT Singers (SingerId, FirstName, LastName)
					VALUES (110, '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{ReturnCommitStats: true}})
	if err != nil {
		return fmt.Errorf("commitStats.ReadWriteTransactionWithOptions: %v", err)
	}
	fmt.Fprintf(w, "%d mutations in transaction\n", resp.CommitStats.MutationCount)
	return nil
}

자바


import com.google.cloud.spanner.CommitResponse;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Mutation;
import com.google.cloud.spanner.Options;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import java.util.Arrays;

public class GetCommitStatsSample {

  static void getCommitStats() {
    // TODO(developer): Replace these variables before running the sample.
    final String projectId = "my-project";
    final String instanceId = "my-instance";
    final String databaseId = "my-database";

    try (Spanner spanner =
        SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
      final DatabaseClient databaseClient = spanner
          .getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId));
      getCommitStats(databaseClient);
    }
  }

  static void getCommitStats(DatabaseClient databaseClient) {
    final CommitResponse commitResponse = databaseClient.writeWithOptions(Arrays.asList(
        Mutation.newInsertOrUpdateBuilder("Albums")
            .set("SingerId")
            .to("1")
            .set("AlbumId")
            .to("1")
            .set("MarketingBudget")
            .to("200000")
            .build(),
        Mutation.newInsertOrUpdateBuilder("Albums")
            .set("SingerId")
            .to("2")
            .set("AlbumId")
            .to("2")
            .set("MarketingBudget")
            .to("400000")
            .build()
    ), Options.commitStats());

    System.out.println(
        "Updated data with " + commitResponse.getCommitStats().getMutationCount() + " mutations.");
  }
}

Node.js

다음 코드는 각 행에 2개의 행과 3개의 열을 삽입하거나 업데이트하므로 returnCommitStats 플래그를 설정하고 변형 수 6을 반환합니다.

// Imports the Google Cloud client library.
const {Spanner} = 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,
});

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

// Instantiate Spanner table objects.
const albumsTable = database.table('Albums');

// Updates rows in the Venues table.
try {
  const [response] = await albumsTable.upsert(
    [
      {SingerId: '1', AlbumId: '1', MarketingBudget: '200000'},
      {SingerId: '2', AlbumId: '2', MarketingBudget: '400000'},
    ],
    {returnCommitStats: true}
  );
  console.log(
    `Updated data with ${response.commitStats.mutationCount} mutations.`
  );
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Transaction;

/**
 * Creates a database and tables for sample data.
 * Example:
 * ```
 * create_database($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function get_commit_stats($instanceId, $databaseId)
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $commitStats = $database->runTransaction(function (Transaction $t) use ($spanner) {
        $t->updateBatch('Albums', [
            [
                'SingerId' => 1,
                'AlbumId' => 1,
                'MarketingBudget' => 200000,
            ],
            [
                'SingerId' => 2,
                'AlbumId' => 2,
                'MarketingBudget' => 400000,
            ]
        ]);
        $t->commit(['returnCommitStats' => true]);
        return $t->getCommitStats();
    });

    print('Updated data with ' . $commitStats['mutationCount'] . ' mutations.' . PHP_EOL);
}

Python

Python 클라이언트 라이브러리는 API를 통해 커밋 통계를 직접 반환하는 대신 Info 수준 정보에서 stdout를 통해 이를 로깅합니다.

다음 코드는 database.log_commit_stats = True를 설정하여 모든 트랜잭션에 커밋 통계 로깅을 사용 설정합니다. 또한 이 코드는 마지막으로 표시된 커밋 응답에 대한 참조를 유지하는 샘플 로거를 구현합니다. 그 다음 mutation_count가 이 응답에서 검색되어 표시됩니다.

def log_commit_stats(instance_id, database_id):
    """Inserts sample data using DML and displays the commit statistics. """
    # By default, commit statistics are logged via stdout at level Info.
    # This sample uses a custom logger to access the commit statistics.
    class CommitStatsSampleLogger(logging.Logger):
        def __init__(self):
            self.last_commit_stats = None
            super().__init__("commit_stats_sample")

        def info(self, msg, *args, **kwargs):
            if kwargs["extra"] and "commit_stats" in kwargs["extra"]:
                self.last_commit_stats = kwargs["extra"]["commit_stats"]
            super().info(msg)

    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id, logger=CommitStatsSampleLogger())
    database.log_commit_stats = True

    def insert_singers(transaction):
        row_ct = transaction.execute_update(
            "INSERT Singers (SingerId, FirstName, LastName) "
            " VALUES (110, 'Virginia', 'Watson')"
        )

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

    database.run_in_transaction(insert_singers)
    commit_stats = database.logger.last_commit_stats
    print(
        "{} mutation(s) in transaction.".format(
            commit_stats.mutation_count
        )
    )

Ruby

다음 코드는 각 행에 2개의 행과 3개의 열을 삽입하거나 업데이트하므로 return_commit_stats 플래그를 설정하고 변형 수 6을 반환합니다.

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

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 }
]
commit_options = { return_commit_stats: true }
resp = client.upsert "Albums", records, commit_options: commit_options
puts "Updated data with #{resp.stats.mutation_count} mutations."

다음 단계