Écritures optimisées pour le débit

Cette page explique comment configurer le délai de validation (écriture) maximal pour optimiser le débit d'écriture dans Spanner.

Présentation

Pour garantir la cohérence des données, Spanner envoie des requêtes d'écriture à tous les réplicas de vote de la base de données. Ce processus de réplication peut avoir des calculs supplémentaires. Pour en savoir plus, consultez Réplication :

Les écritures optimisées pour le débit permettent d'amortir ces calculs en exécutant un groupe d'écritures ensemble. Pour ce faire, Spanner introduit un court délai et collecte un groupe d'écritures qui doivent être envoyés aux mêmes participants votants. L'exécution d'écritures de cette manière peut apporter des améliorations substantielles du débit au prix d'une latence légèrement accrue.

Comportement par défaut

Si vous ne définissez pas de délai de validation, Spanner peut définir un petit délai pour vous s'il pense que cela amortira le coût de vos écritures.

Cas d'utilisation courants

Vous pouvez définir manuellement le délai de vos requêtes d'écriture en fonction de votre des besoins de l'application. Vous pouvez également désactiver les délais de validation pour les applications hautement sensible à la latence en définissant le délai de validation maximal sur 0 ms.

Si vous disposez d'une application tolérante à la latence et que vous souhaitez optimiser le débit, définir un délai de validation plus long améliore considérablement le débit, tout en augmentant la latence pour chaque écriture. Par exemple, si vous chargez de manière groupée une grande quantité de données et que l'application ne se soucie pas de la rapidité avec laquelle Spanner écrit des données individuelles, vous pouvez définir le délai de validation sur une valeur plus longue, par exemple 100 ms. Nous vous recommandons de commencer par une valeur de 100 ms, puis d'ajuster la valeur à la hausse et à la baisse jusqu'à ce que les compromis entre la latence et le débit répondent à vos besoins. Pour la plupart des applications, une valeur comprise entre 20 ms et 100 ms est optimale.

Si vous disposez d'une application sensible à la latence, Spanner l'est également par défaut. En cas de charge de travail éphémère, Spanner peut définir un court délai. Vous pouvez essayer de définir de 0 ms pour déterminer si la latence réduite au prix d'une le débit est raisonnable pour votre application.

Définir des délais de commit mixtes

Vous pouvez configurer différents délais de commit max. sur des sous-ensembles de vos écritures. Dans ce cas, Spanner utilise le délai le plus court pour l'ensemble d'écritures. Toutefois, nous vous recommandons de choisir une seule valeur pour la plupart des cas d'utilisation, car cela permet d'obtenir un comportement plus prévisible.

Limites

Vous pouvez définir un délai de commit compris entre 0 et 500 ms. Définir des délais de validation supérieure à 500 ms entraîne une erreur.

Définir le délai de validation maximal sur les requêtes de validation

Le paramètre "max commit delay" fait partie de la méthode CommitRequest. Vous pouvez accéder à cette méthode avec l'API RPC, l'API REST ou la bibliothèque cliente 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

Surveiller la latence des requêtes d'écriture

Vous pouvez surveiller l'utilisation du processeur et la latence de Spanner à l'aide de la console Google Cloud. Lorsque vous définissez un temps de latence plus long pour vos requêtes d'écriture, vous pouvez constater une diminution potentielle de l'utilisation du processeur, tandis que la latence augmente. Pour en savoir plus sur la latence dans les requêtes Spanner, consultez la section Capturer et visualiser la latence des requêtes de l'API Spanner.