使用批次寫入修改資料

本頁說明 Spanner 批次寫入要求,以及如何使用這類要求修改 Spanner 資料。

您可以使用 Spanner 批次寫入,在 Spanner 資料表中插入、更新或刪除多個資料列。Spanner 批次寫入作業支援低延遲寫入,無須讀取作業,並在批次套用突變時傳回回應。如要使用批次寫入,請將相關變異分組,且系統會以不可分割的形式提交群組中的所有變異。系統會以未指定的順序套用各群組的變異,且這些變異彼此獨立 (非不可分割)。Spanner 不必等待所有變異作業套用完畢才傳送回應,因此批次寫入作業允許部分失敗。您也可以一次執行多個批次寫入作業。詳情請參閱「如何使用批次寫入」。

用途

如果您想提交大量寫入作業,但不需要讀取作業,且不需要所有變動的不可分割交易,Spanner 批次寫入就特別實用。

如要批次處理 DML 要求,請使用批次 DML 修改 Spanner 資料。如要進一步瞭解 DML 和變異之間的差異,請參閱「比較 DML 和變異」。

如果是單一突變要求,建議使用鎖定讀寫交易

限制

Spanner 批次寫入有下列限制:

  • 您無法使用Google Cloud 控制台或 Google Cloud CLI 執行 Spanner 批次寫入作業。只能透過 REST 和 RPC API,以及 Spanner 用戶端程式庫使用。

  • 批次寫入作業不支援防重播機制變異可能會套用多次,而多次套用變異可能會導致失敗。舉例來說,如果重新播放插入變異,可能會產生「已存在」錯誤;如果變異中使用以產生或提交時間戳記為準的鍵,可能會導致資料表中新增其他資料列。建議您將寫入作業的結構設為冪等,避免發生這個問題。

  • 您無法復原已完成的批次寫入要求。您可以取消進行中的批次寫入要求。如果取消進行中的批次寫入作業,系統會復原未完成群組中的變異。已完成群組中的變動會提交至資料庫。

  • 批次寫入要求的大小上限與提交要求相同。詳情請參閱「建立、讀取、更新和刪除資料的限制」。

如何使用批次寫入

如要使用批次寫入,您必須對要修改的資料庫擁有 spanner.databases.write 權限。您可以使用 RESTRPC API 要求呼叫,在單一呼叫中以非不可分割的方式批次寫入突變。

使用批次寫入時,應將下列變動類型分組:

  • 在父項和子項資料表中插入具有相同主鍵前置字串的資料列。
  • 將資料列插入資料表,且資料表之間有外鍵關係。
  • 其他相關變動類型,視資料庫結構定義和應用程式邏輯而定。

您也可以使用 Spanner 用戶端程式庫批次寫入資料。 以下程式碼範例會使用新資料列更新 Singers 資料表。

用戶端程式庫

C++

namespace spanner = ::google::cloud::spanner;
// Use upserts as mutation groups are not replay protected.
auto commit_results = client.CommitAtLeastOnce({
    // group #0
    spanner::Mutations{
        spanner::InsertOrUpdateMutationBuilder(
            "Singers", {"SingerId", "FirstName", "LastName"})
            .EmplaceRow(16, "Scarlet", "Terry")
            .Build(),
    },
    // group #1
    spanner::Mutations{
        spanner::InsertOrUpdateMutationBuilder(
            "Singers", {"SingerId", "FirstName", "LastName"})
            .EmplaceRow(17, "Marc", "")
            .EmplaceRow(18, "Catalina", "Smith")
            .Build(),
        spanner::InsertOrUpdateMutationBuilder(
            "Albums", {"SingerId", "AlbumId", "AlbumTitle"})
            .EmplaceRow(17, 1, "Total Junk")
            .EmplaceRow(18, 2, "Go, Go, Go")
            .Build(),
    },
});
for (auto& commit_result : commit_results) {
  if (!commit_result) throw std::move(commit_result).status();
  std::cout << "Mutation group indexes [";
  for (auto index : commit_result->indexes) std::cout << " " << index;
  std::cout << " ]: ";
  if (commit_result->commit_timestamp) {
    auto const& ts = *commit_result->commit_timestamp;
    std::cout << "Committed at " << ts.get<absl::Time>().value();
  } else {
    std::cout << commit_result->commit_timestamp.status();
  }
  std::cout << "\n";
}

C#

// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


using Google.Cloud.Spanner.Data;
using Google.Rpc;
using Google.Cloud.Spanner.V1;
using Google.Protobuf.WellKnownTypes;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class BatchWriteAtLeastOnceAsyncSample
{
    public async Task BatchWriteAtLeastOnceAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString);
        await connection.OpenAsync();

        // 1. Create a SpannerBatchWriteCommand.
        var cmd = connection.CreateBatchWriteCommand();

        // 2. Create and add mutation groups.
        // Mutation group 1: Insert or update a singer (16).
        var cmdSinger1 = connection.CreateInsertOrUpdateCommand("Singers", new SpannerParameterCollection
        {
            { "SingerId", SpannerDbType.Int64, 16 },
            { "FirstName", SpannerDbType.String, "Scarlet" },
            { "LastName", SpannerDbType.String, "Terry" }
        });
        cmd.Add(cmdSinger1);

        // Mutation group 2: Insert or update multiple singers and albums together.
        var cmdSinger2 = connection.CreateInsertOrUpdateCommand("Singers", new SpannerParameterCollection
        {
            { "SingerId", SpannerDbType.Int64, 17 },
            { "FirstName", SpannerDbType.String, "Marc" },
            { "LastName", SpannerDbType.String, "Smith" }
        });

        var cmdSinger3 = connection.CreateInsertOrUpdateCommand("Singers", new SpannerParameterCollection
        {
            { "SingerId", SpannerDbType.Int64, 18 },
            { "FirstName", SpannerDbType.String, "Catalina" },
            { "LastName", SpannerDbType.String,