Operasi tulis yang dioptimalkan throughput

Halaman ini menjelaskan cara mengonfigurasi waktu tunda commit (tulis) maksimum untuk mengoptimalkan throughput operasi tulis di Spanner.

Ringkasan

Untuk memastikan konsistensi data, Spanner mengirimkan permintaan tulis ke semua replika pemungutan suara di database. Proses replikasi ini dapat memiliki overhead komputasi. Untuk mengetahui informasi selengkapnya, lihat Replikasi.

Penulisan yang dioptimalkan throughput memberikan opsi untuk mengamortisasi biaya komputasi ini dengan mengeksekusi sekelompok penulisan secara bersamaan. Untuk melakukannya, Spanner melakukan penundaan singkat dan mengumpulkan sekelompok operasi tulis yang perlu dikirim ke peserta pemungutan suara yang sama. Mengeksekusi penulisan dengan cara ini dapat memberikan peningkatan throughput yang signifikan dengan mengorbankan sedikit peningkatan latensi.

Perilaku default

Jika Anda tidak menetapkan waktu penundaan commit, Spanner mungkin akan sedikit menunda jika menurutnya hal tersebut akan mengamortisasi biaya penulisan Anda.

Kasus penggunaan umum

Anda dapat menetapkan waktu tunda permintaan operasi tulis secara manual, bergantung pada kebutuhan aplikasi Anda. Anda juga dapat menonaktifkan penundaan commit untuk aplikasi yang sangat sensitif terhadap latensi dengan menetapkan waktu penundaan commit maksimum ke 0 milidetik.

Jika Anda memiliki aplikasi yang toleran terhadap latensi dan ingin mengoptimalkan throughput, menetapkan waktu penundaan commit yang lebih lama akan meningkatkan throughput secara signifikan sekaligus menyebabkan latensi yang lebih tinggi untuk setiap penulisan. Misalnya, jika Anda memuat data dalam jumlah besar secara massal dan aplikasi tidak peduli seberapa cepat Spanner menulis data apa pun, maka Anda dapat menetapkan waktu tunda commit ke nilai yang lebih lama, seperti 100 milidetik. Sebaiknya mulai dengan nilai 100 milidetik, lalu naikkan dan turunkan hingga penyeimbangan latensi dan throughput memenuhi kebutuhan Anda. Untuk sebagian besar aplikasi, nilai antara 20 md dan 100 md adalah nilai yang paling baik.

Jika Anda memiliki aplikasi yang sensitif terhadap latensi, Spanner juga juga sensitif terhadap latensi secara default. Jika Anda memiliki beban kerja yang melonjak, Spanner mungkin menetapkan penundaan singkat. Anda dapat bereksperimen dengan menetapkan nilai 0 md untuk menentukan apakah latensi yang berkurang dengan mengorbankan peningkatan throughput wajar untuk aplikasi Anda.

Menetapkan waktu penundaan commit campuran

Anda dapat mengonfigurasi waktu penundaan commit maks yang berbeda di subset penulisan Anda. Jika Anda melakukannya, Spanner akan menggunakan waktu tunda tersingkat yang dikonfigurasi untuk kumpulan operasi tulis. Namun, sebaiknya pilih satu nilai untuk sebagian besar kasus penggunaan karena akan menghasilkan perilaku yang lebih dapat diprediksi.

Batasan

Anda dapat menetapkan waktu penundaan commit antara 0 dan 500 milidetik. Menetapkan penundaan commit yang lebih tinggi dari 500 milidetik akan menghasilkan error.

Menetapkan penundaan commit maksimal pada permintaan commit

Parameter penundaan commit maks adalah bagian dari metode CommitRequest. Anda dapat mengakses metode ini dengan RPC API, REST API, atau menggunakan library klien 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

Memantau latensi permintaan tulis

Anda dapat memantau pemakaian dan latensi CPU Spanner menggunakan Konsol Google Cloud. Saat menetapkan waktu tunda yang lebih lama untuk permintaan tulis, Anda akan melihat kemungkinan penggunaan CPU akan menurun, sementara latensi meningkat. Untuk mempelajari latensi dalam permintaan Spanner, lihat Merekam dan memvisualisasikan latensi permintaan Spanner API.