Recuperare le statistiche di commit per una transazione

Per aiutarti a comprendere, ottimizzare e diagnosticare meglio i problemi relativi alle transazioni, Spanner ti consente di accedere alle statistiche di commit delle transazioni. Al momento, puoi recuperare il numero totale di modifiche per una transazione.

Quando utilizzare le statistiche sui commit

Conoscere il conteggio delle mutazioni per una transazione può essere utile nei seguenti scenari.

Ottimizzare per i viaggi di andata e ritorno

Per migliorare le prestazioni dell'applicazione, puoi ridurre il numero di round trip al database eseguendo il maggior numero possibile di operazioni in ogni transazione. In questo scenario, vuoi massimizzare il numero di mutazioni per transazione, rimanendo al contempo entro i limiti del sistema.

Per determinare quante righe puoi eseguire il commit per transazione rimanendo al di sotto del limite, esegui prima il commit di una riga in una transazione. In questo modo, ottieni una base di riferimento per il conteggio delle mutazioni per riga. Quindi dividi il limite di sistema per la base di riferimento per ottenere un numero di righe per transazione. Per ulteriori informazioni su come vengono conteggiate le mutazioni, consulta questa nota.

Tieni presente che l'ottimizzazione per i viaggi di andata e ritorno non è sempre vantaggiosa, soprattutto se comporta un maggior numero di contese dei blocchi. Puoi risolvere i conflitti di blocco nel tuo database utilizzando le statistiche sui blocchi.

Monitorare le transazioni per evitare di raggiungere i limiti del sistema

Man mano che l'utilizzo dell'applicazione aumenta, è possibile che aumenti anche il numero di mutazioni nella transazione. Per evitare di raggiungere il limite del sistema e che la transazione alla fine non vada a buon fine, puoi monitorare in modo proattivo la statistica di commit del conteggio delle mutazioni nel tempo. Se noti che questo valore aumenta per la stessa transazione, potrebbe essere il momento di ottimizzare nuovamente la transazione come descritto nella sezione precedente.

Come accedere alle statistiche sui commit

Le statistiche sui commit non vengono restituite per impostazione predefinita. Devi invece impostare il flag return_commit_stats su true in ogni CommitRequest. Se il tentativo di commit supera il numero massimo consentito di mutazioni per una transazione, il commit non va a buon fine e viene restituito un errore INVALID_ARGUMENT.

Ecco un esempio di come restituire le statistiche di commit utilizzando le librerie client Spanner.

Recuperare le statistiche di commit

Il seguente esempio mostra come ottenere le statistiche sui commit utilizzando le librerie client Spanner.

C++

Il seguente codice chiama set_return_stats() su CommitOptions e restituisce un conteggio delle mutazioni pari a 6, perché inseriamo o aggiorniamo 2 righe e 3 colonne in ogni riga.

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::move(commit).status();
  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#

In C#, le statistiche sui commit non vengono restituite direttamente tramite l'API. ma vengono registrati al livello di log Informazioni dal logger predefinito.

Il codice seguente attiva la registrazione delle statistiche di commit per tutte le transazioni impostando la proprietà LogCommitStats su SpannerConnectionStringBuilder su true. Il codice implementa anche un logger di esempio che mantiene un riferimento all'ultima risposta di commit visualizzata. MutationCount viene quindi recuperato da questa risposta e visualizzato.


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);
    }
}

Vai

Il seguente codice imposta il flag ReturnCommitStats e stampa il conteggio delle mutazioni quando la transazione viene eseguita correttamente.


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: %w", 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: %w", err)
	}
	fmt.Fprintf(w, "%d mutations in transaction\n", resp.CommitStats.MutationCount)
	return nil
}

Java


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

Il seguente codice imposta il flag returnCommitStats e restituisce un conteggio delle mutazioni pari a 6, perché stiamo inserendo o aggiornando 2 righe e 3 colonne in ogni riga.

// 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(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $commitStats = $database->runTransaction(function (Transaction $t) {
        $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

Anziché restituire le statistiche sui commit direttamente tramite l'API, la libreria client Python le registra utilizzando stdout al livello Info.

Il codice seguente attiva la registrazione delle statistiche di commit per tutte le transazioni impostando database.log_commit_stats = True. Il codice implementa anche un logger di esempio che mantiene un riferimento all'ultima risposta del commit visualizzata. Il mutation_count viene quindi recuperato da questa risposta e visualizzato.

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

Il seguente codice imposta il flag return_commit_stats e restituisce un conteggio delle mutazioni pari a 6, perché stiamo inserendo o aggiornando 2 righe e 3 colonne in ogni riga.

# 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."

Passaggi successivi