Stempel waktu commit di database dialek GoogleSQL

Dokumen ini menjelaskan cara menulis stempel waktu commit untuk setiap operasi penyisipan dan pembaruan yang Anda lakukan dengan Spanner. Untuk menggunakan fitur ini, tetapkan opsi allow_commit_timestamp pada kolom TIMESTAMP, lalu tulis stempel waktu sebagai bagian dari setiap transaksi.

Ringkasan

Stempel waktu commit, berdasarkan TrueTime teknologi, adalah waktu saat transaksi di-commit dalam database. Opsi kolom allow_commit_timestamp memungkinkan Anda menyimpan stempel waktu commit secara atomik ke dalam kolom. Dengan menggunakan stempel waktu commit yang disimpan dalam tabel, Anda dapat menentukan urutan mutasi yang tepat dan membuat fitur seperti log perubahan.

Untuk menyisipkan stempel waktu commit di database, selesaikan langkah-langkah berikut:

  1. Buat kolom dengan jenis TIMESTAMP dengan opsi kolom allow_commit_timestamp yang ditetapkan ke true dalam definisi skema. Misalnya:

    CREATE TABLE Performances (
        ...
        LastUpdateTime  TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
        ...
    ) PRIMARY KEY (...);
    
  2. Jika Anda melakukan penyisipan atau pembaruan dengan DML, gunakan fungsi PENDING_COMMIT_TIMESTAMP untuk menulis stempel waktu commit.

    Jika Anda melakukan penyisipan atau pembaruan dengan mutasi, gunakan string placeholder spanner.commit_timestamp() pada penyisipan atau pembaruan ke kolom stempel waktu commit. Anda juga dapat menggunakan konstanta stempel waktu commit yang disediakan oleh library klien. Misalnya, konstanta ini di klien Java adalah Value.COMMIT_TIMESTAMP.

Saat Spanner melakukan commit transaksi menggunakan placeholder ini sebagai nilai kolom, stempel waktu commit sebenarnya akan ditulis ke kolom yang ditentukan (Misalnya: kolom LastUpdateTime). Anda kemudian dapat menggunakan nilai kolom ini untuk membuat histori pembaruan ke tabel.

Nilai stempel waktu commit tidak dijamin unik. Transaksi yang menulis ke kumpulan kolom yang tidak tumpang-tindih mungkin memiliki stempel waktu yang sama. Transaksi yang menulis ke kumpulan kolom yang tumpang-tindih memiliki stempel waktu yang unik.

Stempel waktu commit Spanner memiliki granularitas mikrodetik, dan dikonversi menjadi nanodetik saat disimpan di kolom TIMESTAMP.

Membuat dan menghapus kolom stempel waktu commit

Gunakan opsi kolom allow_commit_timestamp untuk menambahkan dan menghapus dukungan untuk stempel waktu commit:

  • Saat membuat tabel baru untuk menentukan bahwa kolom mendukung stempel waktu commit.
  • Saat mengubah tabel yang ada:
    • untuk menambahkan kolom baru yang mendukung stempel waktu commit,
    • untuk mengubah kolom TIMESTAMP yang ada agar mendukung stempel waktu commit,
    • untuk mengubah kolom TIMESTAMP yang ada guna menghapus dukungan stempel waktu commit

Kunci dan indeks

Anda dapat menggunakan kolom stempel waktu commit sebagai kolom kunci utama atau sebagai kolom non-kunci. Kunci utama dapat ditentukan sebagai ASC atau DESC.

  • ASC (default) - Kunci naik ideal untuk menjawab kueri dari waktu tertentu ke depan.
  • DESC - Kunci turun menyimpan baris terbaru di bagian atas tabel. Kunci ini memberikan akses cepat ke catatan terbaru.

Opsi allow_commit_timestamp harus konsisten di seluruh kunci utama tabel induk dan turunan. Jika opsi tidak konsisten di seluruh kunci utama, Spanner akan menampilkan error. Satu-satunya saat opsi dapat tidak konsisten adalah saat Anda membuat atau memperbarui skema.

Menggunakan stempel waktu commit dalam skenario berikut akan membuat hotspot yang mengurangi performa data:

  • Kolom stempel waktu commit sebagai bagian pertama dari kunci utama tabel:

    CREATE TABLE Users (
      LastAccess TIMESTAMP NOT NULL,
      UserId     INT64 NOT NULL,
      ...
    ) PRIMARY KEY (LastAccess, UserId);
    
  • Bagian pertama dari kunci utama indeks sekunder:

    CREATE INDEX UsersByLastAccess ON Users(LastAccess)
    

    atau

    CREATE INDEX UsersByLastAccessAndName ON Users(LastAccess, FirstName)
    

Hotspot mengurangi performa data, bahkan dengan kecepatan tulis yang rendah. Tidak ada overhead performa jika stempel waktu commit diaktifkan pada kolom non-kunci yang tidak diindeks.

Membuat kolom stempel waktu commit

DDL berikut membuat tabel dengan kolom yang mendukung stempel waktu commit.

CREATE TABLE Performances (
    SingerId        INT64 NOT NULL,
    VenueId         INT64 NOT NULL,
    EventDate       Date,
    Revenue         INT64,
    LastUpdateTime  TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (SingerId, VenueId, EventDate),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE

Menambahkan opsi akan mengubah kolom stempel waktu sebagai berikut:

  • Anda dapat menggunakan string placeholder spanner.commit_timestamp() (atau konstanta yang disediakan oleh library klien) untuk penyisipan dan pembaruan.
  • Kolom hanya dapat berisi nilai di masa lalu. Untuk mengetahui informasi selengkapnya, lihat Memberikan nilai Anda sendiri untuk stempel waktu.

Opsi allow_commit_timestamp peka huruf besar/kecil.

Menambahkan kolom stempel waktu commit ke tabel yang ada

Untuk menambahkan kolom stempel waktu commit ke tabel yang ada, gunakan pernyataan ALTER TABLE. Misalnya, untuk menambahkan kolom LastUpdateTime ke tabel Performances, gunakan pernyataan berikut:

ALTER TABLE Performances ADD COLUMN LastUpdateTime TIMESTAMP
    NOT NULL OPTIONS (allow_commit_timestamp=true)

Mengonversi kolom stempel waktu menjadi kolom stempel waktu commit

Anda dapat mengonversi kolom stempel waktu yang ada menjadi kolom stempel waktu commit, tetapi tindakan ini mengharuskan Spanner memvalidasi bahwa nilai stempel waktu yang ada berada di masa lalu. Misalnya:

ALTER TABLE Performances ALTER COLUMN LastUpdateTime
    SET OPTIONS (allow_commit_timestamp=true)

Anda tidak dapat mengubah jenis data atau anotasi NULL kolom dalam pernyataan ALTER TABLE yang menyertakan SET OPTIONS. Untuk mengetahui detailnya, lihat Bahasa Definisi Data.

Menghapus opsi stempel waktu commit

Jika Anda ingin menghapus dukungan stempel waktu commit dari kolom, gunakan opsi allow_commit_timestamp=null dalam pernyataan ALTER TABLE. Perilaku stempel waktu commit akan dihapus, tetapi kolom tersebut masih berupa stempel waktu. Mengubah opsi tidak akan mengubah karakteristik kolom lainnya, seperti jenis atau nullability (NOT NULL). Misalnya:

ALTER TABLE Performances ALTER COLUMN LastUpdateTime
    SET OPTIONS (allow_commit_timestamp=null)

Menulis stempel waktu commit menggunakan pernyataan DML

Anda menggunakan fungsi PENDING_COMMIT_TIMESTAMP untuk menulis stempel waktu commit dalam pernyataan DML. Spanner memilih stempel waktu commit saat transaksi di-commit.

Pernyataan DML berikut memperbarui kolom LastUpdateTime dalam tabel Performances dengan stempel waktu commit:

UPDATE Performances SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()
   WHERE SingerId=1 AND VenueId=2 AND EventDate="2015-10-21"

Contoh kode berikut menggunakan fungsi PENDING_COMMIT_TIMESTAMP untuk menulis stempel waktu commit di kolom LastUpdateTime.

C++

void DmlStandardUpdateWithTimestamp(google::cloud::spanner::Client client) {
  using ::google::cloud::StatusOr;
  namespace spanner = ::google::cloud::spanner;
  auto commit_result = client.Commit(
      [&client](spanner::Transaction txn) -> StatusOr<spanner::Mutations> {
        auto update = client.ExecuteDml(
            std::move(txn),
            spanner::SqlStatement(
                "UPDATE Albums SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()"
                "  WHERE SingerId = 1"));
        if (!update) return std::move(update).status();
        return spanner::Mutations{};
      });
  if (!commit_result) throw std::move(commit_result).status();
  std::cout << "Update was successful "
            << "[spanner_dml_standard_update_with_timestamp]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System;
using System.Threading.Tasks;

public class UpdateUsingDmlWithTimestampCoreAsyncSample
{
    public async Task<int> UpdateUsingDmlWithTimestampCoreAsync(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();

        using var cmd = connection.CreateDmlCommand("UPDATE Albums SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1");
        int rowCount = await cmd.ExecuteNonQueryAsync();

        Console.WriteLine($"{rowCount} row(s) updated...");
        return rowCount;
    }
}

Go


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
)

func updateUsingDMLWithTimestamp(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
		stmt := spanner.Statement{
			SQL: `UPDATE Albums
				SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()
				WHERE SingerId = 1`,
		}
		rowCount, err := txn.Update(ctx, stmt)
		if err != nil {
			return err
		}
		fmt.Fprintf(w, "%d record(s) updated.\n", rowCount)
		return nil
	})
	return err
}

Java

static void updateUsingDmlWithTimestamp(DatabaseClient dbClient) {
  dbClient
      .readWriteTransaction()
      .run(transaction -> {
        String sql =
            "UPDATE Albums "
                + "SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1";
        long rowCount = transaction.executeUpdate(Statement.of(sql));
        System.out.printf("%d records updated.\n", rowCount);
        return null;
      });
}

Node.js

// Imports the Google Cloud client library
const {Spanner} = 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,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const