Daten mit Batchschreibvorgang ändern

Auf dieser Seite werden Spanner-Batchschreibanfragen beschrieben. Außerdem erfahren Sie, wie Sie sie zum Ändern Ihrer Spanner-Daten verwenden können.

Mit dem Batchschreibvorgang von Spanner können Sie mehrere Zeilen in Ihren Spanner-Tabellen einfügen, aktualisieren oder löschen. Spanner-Batchschreibvorgang unterstützt Schreibvorgänge mit niedriger Latenz ohne Lesevorgang und gibt Antworten zurück, wenn Mutationen in Batches angewendet werden. Wenn Sie Batchschreibvorgänge verwenden möchten, gruppieren Sie verwandte Mutationen, woraufhin alle Mutationen in einer Gruppe in kleinstmöglichen Schritten in kleinstmöglichen Schritten übergeben werden. Die gruppenübergreifenden Mutationen werden in einer nicht spezifizierten Reihenfolge angewendet und sind unabhängig voneinander (nicht atomar). Spanner muss nicht warten, bis alle Mutationen angewendet wurden, bevor eine Antwort gesendet wird. Das bedeutet, dass der Batchschreibvorgang Teilfehler ermöglicht. Sie können auch mehrere Batch-Schreibvorgänge gleichzeitig ausführen. Weitere Informationen finden Sie unter Batchschreibvorgang verwenden.

Anwendungsfälle

Spanner-Batchschreibvorgang ist besonders nützlich, wenn Sie einen Commit für eine große Anzahl von Schreibvorgängen ohne Lesevorgang ausführen möchten, aber keine atomare Transaktion für alle Ihre Mutationen erforderlich sind.

Wenn Sie Ihre DML-Anfragen in Batches zusammenfassen möchten, verwenden Sie Batch-DML, um die Spanner-Daten zu ändern. Weitere Informationen zu den Unterschieden zwischen DML und Mutationen finden Sie unter DML und Mutationen vergleichen.

Für einzelne Mutationsanfragen empfehlen wir die Verwendung einer sperrenden Lese-Schreib-Transaktion.

Beschränkungen

Für Spanner-Batchschreibvorgänge gelten die folgenden Einschränkungen:

  • Das Schreiben von Spanner-Batchschreibvorgängen ist über die Google Cloud Console oder die Google Cloud CLI nicht möglich. Sie ist nur mit REST API und RPC API und der Spanner-Java-Clientbibliothek verfügbar.

  • Der Schutz vor Wiedergabe wird bei Verwendung von Batchschreibvorgängen nicht unterstützt. Es ist möglich, dass Mutationen mehr als einmal angewendet werden. Eine Mutation, die mehr als einmal angewendet wird, kann zu einem Fehler führen. Wenn beispielsweise eine Mutation vom Typ „Insert“ wiederholt wird, kann dies zu einem bereits vorhandenen Fehler führen. Wenn Sie in der Mutation generierte oder zeitstempelbasierte Schlüssel verwenden oder einen Commit durchführen, werden der Tabelle möglicherweise zusätzliche Zeilen hinzugefügt. Wir empfehlen, Ihre Schreibvorgänge idempotent zu strukturieren, um dieses Problem zu vermeiden.

  • Sie können für eine abgeschlossene Batchschreibanfrage kein Rollback durchführen. Sie können eine laufende Batchschreibanfrage abbrechen. Wenn Sie einen laufenden Batchschreibvorgang abbrechen, werden Mutationen in nicht abgeschlossenen Gruppen rückgängig gemacht. Mutationen in abgeschlossenen Gruppen werden in der Datenbank festgeschrieben.

  • Die maximale Größe für eine Batchschreibanfrage entspricht dem Limit für eine Commit-Anfrage. Weitere Informationen finden Sie unter Limits für das Erstellen, Lesen, Aktualisieren und Löschen von Daten.

Batchschreibvorgang verwenden

Wenn Sie Batchschreibvorgänge verwenden möchten, benötigen Sie die Berechtigung spanner.databases.write für die Datenbank, die Sie ändern möchten. Mit einem REST- oder RPC API-Anfrageaufruf können Sie Mutationen in einem Batch nicht atomar in einem einzelnen Aufruf schreiben.

Bei Verwendung des Batch-Schreibvorgangs sollten Sie die folgenden Mutationstypen gruppieren:

  • Zeilen mit demselben Primärschlüsselpräfix sowohl in die übergeordnete als auch die untergeordnete Tabelle einfügen
  • Einfügen von Zeilen in Tabellen mit einer Fremdschlüsselbeziehung zwischen den Tabellen.
  • Andere Arten verwandter Mutationen, die vom Datenbankschema und der Anwendungslogik abhängen.

Sie können auch Batchschreibvorgänge mit der Spanner-Java-Clientbibliothek ausführen. Im folgenden Codebeispiel wird die Tabelle Singers mit neuen Zeilen aktualisiert.

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

Nächste Schritte