Gravações com capacidade de processamento otimizada

Nesta página, descrevemos como configurar o tempo máximo de atraso de confirmação (gravação) para otimizar a capacidade de gravação no Spanner.

Visão geral

Para garantir a consistência dos dados, o Spanner envia solicitações de gravação para todas as réplicas de votação no banco de dados. Esse processo de replicação pode ter sobrecarga computacional. Para mais informações, consulte Replicação.

As gravações otimizadas de capacidade oferecem a opção de amortizar esses custos de computação executando um grupo de gravações juntas. Para fazer isso, o Spanner apresenta um pequeno atraso e coleta um grupo de gravações que precisam ser enviadas aos mesmos participantes da votação. Executar gravações dessa maneira pode proporcionar melhorias substanciais de capacidade ao custo de um pouco aumento de latência.

Comportamento padrão

Se você não definir um tempo de atraso de confirmação, o Spanner poderá definir um pequeno atraso se achar que isso vai amortizar o custo das gravações.

Casos de uso comuns

É possível definir manualmente o tempo de atraso das solicitações de gravação, dependendo das necessidades do aplicativo. Também é possível desativar atrasos de confirmação para aplicativos altamente sensíveis à latência definindo o tempo máximo de atraso de confirmação como 0 ms.

Se você tiver um aplicativo tolerante à latência e quiser otimizar a capacidade, definir um tempo de atraso de confirmação mais longo melhora significativamente a capacidade ao mesmo tempo que gera uma latência maior para cada gravação. Por exemplo, se você estiver carregando em massa uma grande quantidade de dados e o aplicativo não se importar com a rapidez com que o Spanner grava dados individuais, será possível definir o tempo de atraso de confirmação como um valor mais longo, como 100 ms. Recomendamos que você comece com um valor de 100 ms e aumente ou diminua até que as compensações de latência e capacidade atendam às suas necessidades. Para a maioria dos aplicativos, um valor entre 20 ms e 100 ms funciona melhor.

Se você tiver um aplicativo sensível à latência, o do Spanner também será sensível à latência por padrão. Se você tiver uma carga de trabalho com picos, o Spanner poderá definir um pequeno atraso. Tente definir um valor de 0 ms para determinar se a latência reduzida ao custo de uma capacidade maior é razoável para seu aplicativo.

Definir tempos de atraso de confirmação mistos

Você pode configurar diferentes tempos de atraso de confirmação em subconjuntos de suas gravações. Se você fizer isso, o Spanner usará o menor tempo de atraso configurado para o conjunto de gravações. No entanto, recomendamos escolher um único valor para a maioria dos casos de uso, porque isso resulta em um comportamento mais previsível.

Limitações

É possível definir um tempo de atraso de confirmação entre 0 e 500 ms. A definição de atrasos de confirmação superiores a 500 ms resulta em erro.

Definir atraso máximo de confirmação em solicitações de confirmação

O parâmetro de atraso máximo de commit faz parte do método CommitRequest. Acesse esse método com a API RPC, a API REST ou usando a biblioteca de cliente do 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

Monitorar a latência da solicitação de gravação

É possível monitorar a utilização e a latência da CPU do Spanner usando o console do Google Cloud. Quando você definir um tempo de atraso mais longo para suas solicitações de gravação, a utilização da CPU poderá diminuir potencialmente enquanto a latência aumenta. Para saber mais sobre a latência nas solicitações do Spanner, consulte Capturar e visualizar a latência da solicitação da API Spanner Spanner.