Esegui il commit dei timestamp nei database di dialetti GoogleSQL

Questo argomento descrive come scrivere un timestamp di commit per ogni inserimento e aggiornamento dell'operazione che esegui con Spanner. Per utilizzare questa funzione, imposta il parametro Opzione allow_commit_timestamp su una colonna TIMESTAMP, poi scrivi il timestamp come parte di ogni transazione.

Panoramica

Il timestamp del commit, basato su TrueTime. tecnologia, è il momento in cui del commit della transazione nel database. Colonna allow_commit_timestamp consente di archiviare atomicamente il timestamp di commit in una colonna. Utilizzando i timestamp di commit archiviati nelle tabelle, puoi determinare l'ordine delle mutazioni e le funzionalità di creazione come i log delle modifiche.

Per inserire timestamp di commit nel database:

  1. Crea una colonna di tipo TIMESTAMP con la colonna. opzione allow_commit_timestamp impostata su true nella definizione dello schema. Per esempio:

    CREATE TABLE Performances (
        ...
        LastUpdateTime  TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
        ...
    ) PRIMARY KEY (...);
    
  2. Se esegui inserimenti o aggiornamenti con DML, usa la funzione PENDING_COMMIT_TIMESTAMP per scrivere il commit timestamp.

    Se esegui inserimenti o aggiornamenti con mutazioni, utilizza la stringa segnaposto spanner.commit_timestamp() sugli inserimenti o sugli aggiornamenti del commit colonna timestamp. Puoi utilizzare anche la costante di timestamp di commit fornita dalla libreria client. Ad esempio, questo nel client Java è Value.COMMIT_TIMESTAMP.

Quando Spanner esegue il commit della transazione utilizzando questi segnaposto come colonna , il timestamp di commit effettivo viene scritto nella colonna specificata (Per esempio: la colonna LastUpdateTime). Puoi quindi utilizzare questo valore di colonna per creare una cronologia degli aggiornamenti alla tabella.

Non è garantito che i valori dei timestamp di commit siano univoci. Transazioni che in insiemi di campi non sovrapposti potrebbero avere lo stesso timestamp. Le transazioni che scrivono in insiemi sovrapposti di campi hanno timestamp univoci.

I timestamp di commit di Spanner hanno una granularità in microsecondi, e vengono convertiti in nanosecondi se archiviati in TIMESTAMP colonne.

Crea ed elimina una colonna di timestamp di commit

Utilizza l'opzione della colonna allow_commit_timestamp per aggiungere e rimuovere il supporto per timestamp di commit:

  • Quando crei una nuova tabella per specificare che una colonna supporta i timestamp di commit.
  • Quando modifichi una tabella esistente:
      .
    • per aggiungere una nuova colonna con i timestamp di commit,
    • per modificare una colonna TIMESTAMP esistente per supportare i timestamp di commit,
    • per modificare una colonna TIMESTAMP esistente per rimuovere il timestamp di commit supporto

Chiavi e indici

Puoi utilizzare una colonna del timestamp di commit come colonna di chiave primaria o come non chiave colonna. Le chiavi primarie possono essere definite come ASC o DESC.

  • ASC (predefinito): le chiavi crescenti sono ideali per rispondere a query da un a un determinato periodo di tempo.
  • DESC: le chiavi discendenti consentono di mantenere le righe più recenti nella parte superiore della tabella. Forniscono un accesso rapido ai record più recenti.

L'opzione allow_commit_timestamp deve essere coerente nell'istanza principale delle tabelle padre e figlio. Se l'opzione non è coerente tra primarie, Spanner restituisce un errore. L'unica volta in cui l'opzione può non essere coerente sia quando crei o aggiorni lo schema.

L'uso di timestamp di commit nei seguenti scenari crea hotspot, che ridurre le prestazioni dei dati:

  • Esegui il commit della colonna timestamp come prima parte della chiave primaria di una tabella:

    CREATE TABLE Users (
      LastAccess TIMESTAMP NOT NULL,
      UserId     INT64 NOT NULL,
      ...
    ) PRIMARY KEY (LastAccess, UserId);
    
  • La prima parte della chiave primaria di un indice secondario:

    CREATE INDEX UsersByLastAccess ON Users(LastAccess)
    

    o

    CREATE INDEX UsersByLastAccessAndName ON Users(LastAccess, FirstName)
    

Gli hotspot si riducono le prestazioni dei dati, anche con basse velocità di scrittura. Non c'è alcun overhead del rendimento se i timestamp di commit sono abilitati su colonne non chiave che non sono indicizzate.

Crea una colonna del timestamp di commit

Il seguente DDL crea una tabella con una colonna che supporta il commit i timestamp.

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

Se aggiungi l'opzione, la colonna del timestamp viene modificata come segue:

  • Puoi utilizzare la stringa segnaposto spanner.commit_timestamp() (o una costante fornite dalla libreria client) per inserimenti e aggiornamenti.
  • La colonna può contenere solo valori nel passato. Per ulteriori informazioni, vedi Specifica il tuo valore per il timestamp.

L'opzione allow_commit_timestamp è sensibile alle maiuscole.

Aggiungi una colonna del timestamp di commit a una tabella esistente

Per aggiungere una colonna di timestamp di commit a una tabella esistente, utilizza ALTER TABLE l'Informativa. Ad esempio, per aggiungere una colonna LastUpdateTime a Performances utilizza la seguente istruzione:

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

Converti una colonna timestamp in una colonna di timestamp di commit

Puoi convertire una colonna del timestamp esistente in una colonna del timestamp di commit, ma per farlo è necessario che Spanner verifichi che l'istanza esistente i valori timestamp sono nel passato. Ad esempio:

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

Non puoi modificare il tipo di dati o l'annotazione NULL di una colonna in un Istruzione ALTER TABLE che include SET OPTIONS. Per maggiori dettagli, vedi Data Definition Language.

Rimuovi l'opzione del timestamp di commit

Se vuoi rimuovere il supporto del timestamp di commit da una colonna, utilizza l'opzione allow_commit_timestamp=null in: ALTER TABLE l'Informativa. Il comportamento del timestamp di commit viene rimosso, ma la colonna rimane un timestamp. La modifica dell'opzione non influisce su altre caratteristiche del colonna, ad esempio tipo o supporto di valori null (NOT NULL). Ad esempio:

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

Scrivi un timestamp di commit utilizzando un'istruzione DML

Utilizzerai la funzione PENDING_COMMIT_TIMESTAMP per scrivere il commit in un'istruzione DML. Spanner seleziona il timestamp di commit quando la transazione commit.

La seguente istruzione DML aggiorna la colonna LastUpdateTime in Tabella Performances con il timestamp di commit:

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

Il seguente esempio di codice utilizza la classe PENDING_COMMIT_TIMESTAMP per scrivere il timestamp di commit nella colonna 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;
    }
}

Vai


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 database = instance.database(databaseId);

database.runTransaction(async (err, transaction) => {
  if (err) {
    console.error(err);
    return;
  }
  try {
    const [rowCount] = await transaction.runUpdate({
      sql: `UPDATE Albums
        SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()
        WHERE SingerId = 1`,
    });

    console.log(`Successfully updated ${rowCount} records.`);
    await transaction.commit();
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    // Close the database when finished.
    database.close();
  }
});

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Transaction;

/**
 * Update data with a DML statement using timestamps.
 *
 * The database and table must already exist and can be created using
 * `create_database`.
 * Example:
 * ```
 * insert_data($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function update_data_with_dml_timestamp(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $database->runTransaction(function (Transaction $t) {
        $rowCount = $t->executeUpdate(
            'UPDATE Albums '
            . 'SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1');
        $t->commit();
        printf('Updated %d row(s).' . PHP_EOL, $rowCount);
    });
}

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 update_albums(transaction):
    row_ct = transaction.execute_update(
        "UPDATE Albums "
        "SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() "
        "WHERE SingerId = 1"
    )

    print("{} record(s) updated.".format(row_ct))

database.run_in_transaction(update_albums)

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id
row_count = 0

client.transaction do |transaction|
  row_count = transaction.execute_update(
    "UPDATE Albums SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1"
  )
end

puts "#{row_count} records updated."

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

commit_timestamp = client.commit_timestamp

client.commit do |c|
  c.update "Albums", [
    { SingerId: 1, AlbumId: 1, MarketingBudget: 100_000, LastUpdateTime: commit_timestamp },
    { SingerId: 2, AlbumId: 2, MarketingBudget: 750_000, LastUpdateTime: commit_timestamp }
  ]
end

puts "Updated data"

I timestamp di commit possono essere scritti solo nelle colonne annotate con il Opzione allow_commit_timestamp=true.

Se sono presenti mutazioni sulle righe di più tabelle, devi specificare spanner.commit_timestamp() (o la libreria client costante) per la colonna del timestamp di commit in ogni tabella.

Esegui una query su una colonna del timestamp di commit

L'esempio seguente esegue query sulla colonna del timestamp di commit della tabella.

C++

void QueryDataWithTimestamp(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  spanner::SqlStatement select(
      "SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime"
      "  FROM Albums"
      " ORDER BY LastUpdateTime DESC");
  using RowType =
      std::tuple<std::int64_t, std::int64_t, absl::optional<std::int64_t>,
                 absl::optional<spanner::Timestamp>>;

  auto rows = client.ExecuteQuery(std::move(select));
  for (auto& row : spanner::StreamOf<RowType>(rows)) {
    if (!row) throw std::move(row).status();
    std::cout << std::get<0>(*row) << " " << std::get<1>(*row);
    auto marketing_budget = std::get<2>(*row);
    if (!marketing_budget) {
      std::cout << " NULL";
    } else {
      std::cout << ' ' << *marketing_budget;
    }
    auto last_update_time = std::get<3>(*row);
    if (!last_update_time) {
      std::cout << " NULL";
    } else {
      std::cout << ' ' << *last_update_time;
    }
    std::cout << "\n";
  }
}

C#


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

public class QueryDataWithTimestampColumnAsyncSample
{
    public class Album
    {
        public int SingerId { get; set; }
        public int AlbumId { get; set; }
        public DateTime? LastUpdateTime { get; set; }
        public long? MarketingBudget { get; set; }
    }

    public async Task<List<Album>> QueryDataWithTimestampColumnAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand("SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime FROM Albums ORDER BY LastUpdateTime DESC");

        var albums = new List<Album>();
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            albums.Add(new Album
            {
                SingerId = reader.GetFieldValue<int>("SingerId"),
                AlbumId = reader.GetFieldValue<int>("AlbumId"),
                LastUpdateTime = reader.IsDBNull(reader.GetOrdinal("LastUpdateTime")) ? (DateTime?)null : reader.GetFieldValue<DateTime>("LastUpdateTime"),
                MarketingBudget = reader.IsDBNull(reader.GetOrdinal("MarketingBudget")) ? 0 : reader.GetFieldValue<long>("MarketingBudget")
            });
        }
        return albums;
    }
}

Vai


import (
	"context"
	"fmt"
	"io"
	"strconv"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

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

	stmt := spanner.Statement{
		SQL: `SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime
				FROM Albums ORDER BY LastUpdateTime DESC`}
	iter := client.Single().Query(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var singerID, albumID int64
		var marketingBudget spanner.NullInt64
		var lastUpdateTime spanner.NullTime
		if err := row.ColumnByName("SingerId", &singerID); err != nil {
			return err
		}
		if err := row.ColumnByName("AlbumId", &albumID); err != nil {
			return err
		}
		if err := row.ColumnByName("MarketingBudget", &marketingBudget); err != nil {
			return err
		}
		budget := "NULL"
		if marketingBudget.Valid {
			budget = strconv.FormatInt(marketingBudget.Int64, 10)
		}
		if err := row.ColumnByName("LastUpdateTime", &lastUpdateTime); err != nil {
			return err
		}
		timestamp := "NULL"
		if lastUpdateTime.Valid {
			timestamp = lastUpdateTime.String()
		}
		fmt.Fprintf(w, "%d %d %s %s\n", singerID, albumID, budget, timestamp)
	}
}

Java

static void queryMarketingBudgetWithTimestamp(DatabaseClient dbClient) {
  // Rows without an explicit value for MarketingBudget will have a MarketingBudget equal to
  // null. A try-with-resource block is used to automatically release resources held by
  // ResultSet.
  try (ResultSet resultSet =
      dbClient
          .singleUse()
          .executeQuery(
              Statement.of(
                  "SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime FROM Albums"
                      + " ORDER BY LastUpdateTime DESC"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s %s\n",
          resultSet.getLong("SingerId"),
          resultSet.getLong("AlbumId"),
          // We check that the value is non null. ResultSet getters can only be used to retrieve
          // non null values.
          resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"),
          resultSet.isNull("LastUpdateTime") ? "NULL" : resultSet.getTimestamp("LastUpdateTime"));
    }
  }
}

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 database = instance.database(databaseId);

const query = {
  sql: `SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime
          FROM Albums ORDER BY LastUpdateTime DESC`,
};

// Queries rows from the Albums table
try {
  const [rows] = await database.run(query);

  rows.forEach(row => {
    const json = row.toJSON();

    console.log(
      `SingerId: ${json.SingerId}, AlbumId: ${
        json.AlbumId
      }, MarketingBudget: ${
        json.MarketingBudget ? json.MarketingBudget : null
      }, LastUpdateTime: ${json.LastUpdateTime}`
    );
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;

/**
 * Queries sample data from a database with a commit timestamp column.
 *
 * This sample uses the `MarketingBudget` column. You can add the column
 * by running the `add_column` sample or by running this DDL statement against
 * your database:
 *
 *      ALTER TABLE Albums ADD COLUMN MarketingBudget INT64
 *
 * This sample also uses the 'LastUpdateTime' commit timestamp column. You can
 * add the column by running the `add_timestamp_column` sample or by running
 * this DDL statement against your database:
 *
 * 		ALTER TABLE Albums ADD COLUMN LastUpdateTime TIMESTAMP OPTIONS (allow_commit_timestamp=true)
 *
 * Example:
 * ```
 * query_data_with_timestamp_column($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function query_data_with_timestamp_column(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $results = $database->execute(
        'SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime ' .
        ' FROM Albums ORDER BY LastUpdateTime DESC'
    );

    foreach ($results as $row) {
        if ($row['MarketingBudget'] == null) {
            $row['MarketingBudget'] = 'NULL';
        }
        if ($row['LastUpdateTime'] == null) {
            $row['LastUpdateTime'] = 'NULL';
        }
        printf('SingerId: %s, AlbumId: %s, MarketingBudget: %s, LastUpdateTime: %s' . PHP_EOL,
            $row['SingerId'], $row['AlbumId'], $row['MarketingBudget'], $row['LastUpdateTime']);
    }
}

Python

def query_data_with_timestamp(instance_id, database_id):
    """Queries sample data from the database using SQL.

    This updates the `LastUpdateTime` column which must be created before
    running this sample. You can add the column by running the
    `add_timestamp_column` sample or by running this DDL statement
    against your database:

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

    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)

    database = instance.database(database_id)

    with database.snapshot() as snapshot:
        results = snapshot.execute_sql(
            "SELECT SingerId, AlbumId, MarketingBudget FROM Albums "
            "ORDER BY LastUpdateTime DESC"
        )

    for row in results:
        print("SingerId: {}, AlbumId: {}, MarketingBudget: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

client.execute("SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime
                FROM Albums ORDER BY LastUpdateTime DESC").rows.each do |row|
  puts "#{row[:SingerId]} #{row[:AlbumId]} #{row[:MarketingBudget]} #{row[:LastUpdateTime]}"
end

Specifica un valore per la colonna del timestamp del commit

Puoi fornire un valore personale per la colonna del timestamp di commit, anziché passando spanner.commit_timestamp() (o costante della libreria client) come . Il valore deve essere un timestamp nel passato. Questa restrizione assicura che la scrittura dei timestamp sia un'operazione rapida e poco costosa. Il server restituisce un errore FailedPrecondition se viene specificato un timestamp futuro.

Crea un log delle modifiche

Supponiamo di voler creare un log delle modifiche di ogni mutazione che si verifica a un e utilizzare il log delle modifiche per il controllo. Un esempio potrebbe essere una tabella che archivia la cronologia delle modifiche apportate ai documenti di videoscrittura. Il timestamp di commit semplifica la creazione del log delle modifiche, perché i timestamp possono imporre l'ordine delle voci del log delle modifiche. Potresti creare un che archivia la cronologia delle modifiche apportate a un determinato documento utilizzando uno schema come nell'esempio seguente:

CREATE TABLE Documents (
  UserId     INT64 NOT NULL,
  DocumentId INT64 NOT NULL,
  Contents   STRING(MAX) NOT NULL,
) PRIMARY KEY (UserId, DocumentId);

CREATE TABLE DocumentHistory (
  UserId     INT64 NOT NULL,
  DocumentId INT64 NOT NULL,
  Ts         TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
  Delta      STRING(MAX),
) PRIMARY KEY (UserId, DocumentId, Ts),
  INTERLEAVE IN PARENT Documents ON DELETE NO ACTION;

Per creare un log delle modifiche, inserisci una nuova riga in DocumentHistory nella stessa transazione in cui inserisci o aggiorni una riga in Document. Durante l'inserimento della nuova riga in DocumentHistory, usa il segnaposto spanner.commit_timestamp() (o costante della libreria client) per indicare Spanner per scrivere il timestamp di commit nella colonna Ts. Interfoliazione la tabella DocumentsHistory con la tabella Documents consentirà di inserire località e di inserimenti e aggiornamenti più efficienti. Tuttavia, aggiunge anche il vincolo che le righe padre e figlio devono essere eliminati insieme. Per mantenere le righe in DocumentHistory dopo le righe in Documents vengono eliminate, non alternare le tabelle.

Ottimizza le query sui dati recenti con timestamp di commit

I timestamp di commit consentono un'ottimizzazione Spanner che può ridurre l'I/O delle query quando si recuperano dati scritti dopo una nel tempo.

Per attivare questa ottimizzazione, la clausola WHERE di una query deve includere un confronto tra la colonna del timestamp di commit di una tabella e un intervallo di tempo specifico che fornisci, con i seguenti attributi:

  • Specifica il tempo specifico come espressione costante: un valore letterale, una o una funzione i cui argomenti restituiscono costanti.

  • Confronta se il timestamp di commit è più recente del timestamp in un dato momento, tramite gli operatori > o >=.

  • Se vuoi, aggiungi ulteriori limitazioni alla clausola WHERE con AND. L'estensione della clausola con OR rende la query non idonea e ottimizzazione.

Ad esempio, considera la seguente tabella Performances, che include una colonna con timestamp di 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);

Questa query trae vantaggio dall'ottimizzazione del timestamp del commit descritta in precedenza, perché ha un confronto maggiore o uguale a la colonna del timestamp di commit della tabella e un'espressione costante, in questo case, un valore letterale:

SELECT * FROM Performances WHERE LastUpdateTime >= "2022-05-01";

Anche la seguente query è idonea per l'ottimizzazione, poiché ha un Confronto di maggiore di tra il timestamp di commit e una funzione i cui argomenti restituiscono tutti delle costanti durante l'esecuzione della query:

SELECT * FROM Performances
  WHERE LastUpdateTime > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY);

Passaggi successivi