Modifier des données à l'aide de l'écriture par lot

Cette page décrit les requêtes d'écriture par lot Spanner et vous explique comment les utiliser modifier vos données Spanner.

Vous pouvez utiliser l'écriture par lot Spanner pour insérer, mettre à jour ou supprimer plusieurs lignes dans vos tables Spanner. Spanner l'écriture par lot accepte les écritures à faible latence sans opération de lecture, et renvoie à mesure que les mutations sont appliquées par lot. Pour utiliser l'écriture par lot, vous regroupez mutations associées, et validation de toutes les mutations d'un groupe de manière atomique. Les mutations entre les groupes sont appliquées dans un ordre non spécifié et sont indépendants les uns des autres (non atomiques). Spanner n'a pas besoin d'attendre l'application de toutes les mutations avant d'envoyer une réponse. Autrement dit, cette écriture par lot autorise un échec partiel. Vous pouvez aussi exécuter plusieurs les écritures à la fois. Pour en savoir plus, consultez la section Utiliser l'écriture par lot.

Cas d'utilisation

L'écriture par lot Spanner est particulièrement utile si vous souhaitez valider un grand nombre d'écritures sans opération de lecture, mais ne nécessitent pas pour toutes vos mutations.

Si vous souhaitez traiter vos requêtes LMD par lot, utilisez le traitement LMD par lot. pour modifier vos données Spanner. Pour en savoir plus sur les différences entre le LMD et les mutations, consultez la page Comparer le LMD et les mutations.

Pour les requêtes de mutation unique, nous vous recommandons d'utiliser un verrouillage en lecture/écriture transaction.

Limites

L'écriture par lot Spanner présente les limites suivantes:

  • L'écriture par lot Spanner n'est pas disponible avec la console Google Cloud ou Google Cloud CLI. Il n'est disponible qu'avec REST et RPC. et la bibliothèque cliente Java Spanner.

  • Protection contre la relecture n'est pas compatible avec l'écriture par lot. Il est possible que les mutations soient appliquée plusieurs fois, et une mutation appliquée plusieurs fois peut peut entraîner une défaillance. Par exemple, si vous relancez une mutation d'insertion, une erreur existe déjà, ou si vous utilisez des valeurs générées ou validées clés basées sur l'horodatage dans la mutation, des lignes supplémentaires peuvent être ajoutées ajoutés au tableau. Nous vous recommandons de structurer vos écritures de façon à ce qu'elles soient idempotentes pour éviter ce problème.

  • Vous ne pouvez pas effectuer un rollback d'une requête d'écriture par lot terminée. Vous pouvez annuler une requête d'écriture par lot en cours. Si vous annulez une écriture par lot en cours, les mutations des groupes non terminés font l'objet d'un rollback. Mutations dans les groupes terminés sont validés dans la base de données.

  • La taille maximale d'une requête d'écriture par lot est identique à la limite d'un fichier commit de la requête. Pour en savoir plus, consultez Limites de création, de lecture, de mise à jour et de suppression de données.

Utiliser l'écriture par lot

Pour utiliser l'écriture par lot, vous devez disposer de l'autorisation spanner.databases.write sur la base de données à modifier. Vous pouvez écrire des mutations par lot de manière non atomique dans un seul appel à l'aide d'un objet REST ; ou API RPC .

Vous devez regrouper les types de mutations suivants lorsque vous utilisez l'écriture par lot:

  • Insérer des lignes avec le même préfixe de clé primaire dans les éléments parent et enfant tableaux.
  • Insérer des lignes dans des tableaux avec une relation de clé étrangère entre les tableaux
  • Autres types de mutations associées en fonction du schéma de votre base de données et logique d'application.

Vous pouvez également effectuer une écriture par lot à l'aide de la bibliothèque cliente Java Spanner. L'exemple de code suivant met à jour la table Singers avec de nouvelles lignes.

Java


import com.google.api.gax.rpc.ServerStream;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Mutation;
import com.google.cloud.spanner.MutationGroup;
import com.google.cloud.spanner.Options;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import com.google.common.collect.ImmutableList;
import com.google.rpc.Code;
import com.google.spanner.v1.BatchWriteResponse;

public class BatchWriteAtLeastOnceSample {

  /***
   * Assume DDL for the underlying database:
   * <pre>{@code
   *   CREATE TABLE Singers (
   *     SingerId   INT64 NOT NULL,
   *     FirstName  STRING(1024),
   *     LastName   STRING(1024),
   *   ) PRIMARY KEY (SingerId)
   *
   *   CREATE TABLE Albums (
   *     SingerId     INT64 NOT NULL,
   *     AlbumId      INT64 NOT NULL,
   *     AlbumTitle   STRING(1024),
   *   ) PRIMARY KEY (SingerId, AlbumId),
   *   INTERLEAVE IN PARENT Singers ON DELETE CASCADE
   * }</pre>
   */

  private static final MutationGroup MUTATION_GROUP1 =
      MutationGroup.of(
          Mutation.newInsertOrUpdateBuilder("Singers")
              .set("SingerId")
              .to(16)
              .set("FirstName")
              .to("Scarlet")
              .set("LastName")
              .to("Terry")
              .build());
  private static final MutationGroup MUTATION_GROUP2 =
      MutationGroup.of(
          Mutation.newInsertOrUpdateBuilder("Singers")
              .set("SingerId")
              .to(17)
              .set("FirstName")
              .to("Marc")
              .build(),
          Mutation.newInsertOrUpdateBuilder("Singers")
              .set("SingerId")
              .to(18)
              .set("FirstName")
              .to("Catalina")
              .set("LastName")
              .to("Smith")
              .build(),
          Mutation.newInsertOrUpdateBuilder("Albums")
              .set("SingerId")
              .to(17)
              .set("AlbumId")
              .to(1)
              .set("AlbumTitle")
              .to("Total Junk")
              .build(),
          Mutation.newInsertOrUpdateBuilder("Albums")
              .set("SingerId")
              .to(18)
              .set("AlbumId")
              .to(2)
              .set("AlbumTitle")
              .to("Go, Go, Go")
              .build());

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

  static void batchWriteAtLeastOnce(String projectId, String instanceId, String databaseId) {
    try (Spanner spanner =
        SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
      DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId);
      final DatabaseClient dbClient = spanner.getDatabaseClient(dbId);

      // Creates and issues a BatchWrite RPC request that will apply the mutation groups
      // non-atomically and respond back with a stream of BatchWriteResponse.
      ServerStream<BatchWriteResponse> responses =
          dbClient.batchWriteAtLeastOnce(
              ImmutableList.of(MUTATION_GROUP1, MUTATION_GROUP2),
              Options.tag("batch-write-tag"));

      // Iterates through the results in the stream response and prints the MutationGroup indexes,
      // commit timestamp and status.
      for (BatchWriteResponse response : responses) {
        if (response.getStatus().getCode() == Code.OK_VALUE) {
          System.out.printf(
              "Mutation group indexes %s have been applied with commit timestamp %s",
              response.getIndexesList(), response.getCommitTimestamp());
        } else {
          System.out.printf(
              "Mutation group indexes %s could not be applied with error code %s and "
                  + "error message %s", response.getIndexesList(),
              Code.forNumber(response.getStatus().getCode()), response.getStatus().getMessage());
        }
      }
    }
  }
}

Étape suivante