Daten mit Batch-Schreiben ändern

Auf dieser Seite werden Spanner-Batch-Schreibanfragen beschrieben und wie Sie damit Ihre Spanner-Daten ändern können.

Mit dem Batch-Schreiben von Spanner können Sie mehrere Zeilen in Ihren Spanner-Tabellen einfügen, aktualisieren oder löschen. Der Batch-Schreibvorgang von Spanner unterstützt Schreibvorgänge mit niedriger Latenz ohne Lesevorgang und gibt Antworten zurück, wenn Mutationen in Batches angewendet werden. Wenn Sie Batch-Schreiben verwenden, gruppieren Sie zusammenhängende Mutationen und alle Mutationen in einer Gruppe werden atomar verbindlich. Die Mutationen werden in einer nicht angegebenen Reihenfolge auf die Gruppen angewendet und sind voneinander unabhängig (nicht atomar). Spanner muss nicht warten, bis alle Mutationen angewendet wurden, bevor eine Antwort gesendet wird. Das bedeutet, dass bei Batch-Schreiben ein teilweiser Fehler möglich ist. Sie können auch mehrere Batch-Schreibvorgänge gleichzeitig ausführen. Weitere Informationen finden Sie unter Batch-Schreiben verwenden.

Anwendungsfälle

Die Batch-Schreibfunktion von Spanner ist besonders nützlich, wenn Sie eine große Anzahl von Schreibvorgängen ohne Lesevorgang bestätigen möchten, für alle Mutationen jedoch keine atomare Transaktion benötigen.

Wenn Sie Ihre DML-Anfragen in einem Batch senden möchten, verwenden Sie Batch-DML, um Ihre 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 Sperrungs-Lese-/Schreibtransaktion.

Beschränkungen

Für die Batch-Schreibvorgänge von Spanner gelten die folgenden Einschränkungen:

  • Batch-Schreiben in Spanner ist nicht über die Console oder die Google Cloud CLI vonGoogle Cloud verfügbar. Sie ist nur mit REST- und RPC-APIs und der Spanner-Java-Clientbibliothek verfügbar.

  • Der Wiederholungsschutz wird bei Batch-Schreiben nicht unterstützt. Mutationen können mehrmals angewendet werden. Dies kann zu einem Fehler führen. Wenn beispielsweise eine Insert-Mutation wiedergegeben wird, kann der Fehler „Element ist bereits vorhanden“ auftreten. Wenn Sie in der Mutation generierte oder auf dem Commit-Zeitstempel basierende Schlüssel verwenden, kann es dazu führen, dass der Tabelle zusätzliche Zeilen hinzugefügt werden. Wir empfehlen, Ihre Schreibvorgänge so zu strukturieren, dass sie idempotent sind, um dieses Problem zu vermeiden.

  • Sie können eine abgeschlossene Batchschreibanfrage nicht rückgängig machen. Sie können einen laufenden Batch-Schreibvorgang abbrechen. Wenn Sie einen laufenden Batch-Write abbrechen, werden die Mutationen in nicht abgeschlossenen Gruppen rückgängig gemacht. Mutationen in abgeschlossenen Gruppen werden in der Datenbank verbindlich festgelegt.

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

Batch-Schreibvorgänge verwenden

Um Batch-Schreibvorgänge zu verwenden, benötigen Sie die Berechtigung spanner.databases.write für die Datenbank, die Sie ändern möchten. Sie können Mutationen mithilfe eines REST- oder RPC API-Anfrageaufrufs nicht atomar in einem einzigen Aufruf im Batch schreiben.

Wenn Sie die Batch-Schreibfunktion verwenden, sollten Sie die folgenden Mutationstypen gruppieren:

  • Zeilen mit demselben Primärschlüsselpräfix sowohl in die übergeordnete als auch in die untergeordnete Tabelle einfügen.
  • Zeilen in Tabellen mit einer Fremdschlüsselbeziehung zwischen den Tabellen einfügen
  • Andere Arten von zugehörigen Mutationen, je nach Datenbankschema und Anwendungslogik.

Sie können auch die Spanner-Java-Clientbibliothek für Batch-Write-Vorgänge verwenden. 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