In diesem Dokument werden Apache Cassandra und Spanner-Konzepte und -Praktiken Dabei wird vorausgesetzt, dass Sie mit Cassandra möchte bestehende Anwendungen migrieren oder neue Anwendungen entwickeln. wenn Sie Spanner als Datenbank verwenden.
Cassandra und Spanner sind beides groß angelegte verteilte Datenbanken, die für Anwendungen entwickelt wurden, die eine hohe Skalierbarkeit und eine geringe Latenz erfordern. Beide Datenbanken können anspruchsvolle NoSQL-Arbeitslasten unterstützen. Spanner bietet jedoch erweiterte Funktionen für die Datenmodellierung, Abfragen und Transaktionsvorgänge. Weitere Informationen dazu, wie Spanner mit NoSQL verbunden ist Datenbankkriterien finden Sie unter Spanner für nicht relationale Arbeitslasten.
Von Cassandra zu Spanner migrieren
Für die Migration von Cassandra zu Spanner können Sie den Cassandra to Spanner Proxy Adapter verwenden. Mit diesem Open-Source-Tool können Sie Arbeitslasten migrieren von Cassandra oder DataStax Enterprise (DSE) zu Spanner Ihre Anwendungslogik zu prüfen.
Wichtige Konzepte
In diesem Abschnitt werden wichtige Cassandra- und Spanner-Konzepte verglichen.
Terminologie
Cassandra | Spanner |
---|---|
Cluster |
Instanz Ein Cassandra-Cluster entspricht einer Spanner-Instanz – einer Sammlung von Servern und Speicherressourcen. Da es sich bei Spanner um einen verwalteten Dienst handelt, müssen Sie die zugrunde liegende Hardware oder Software nicht konfigurieren. Ich Sie müssen nur die Anzahl der Knoten angeben, die Sie für die Instanz reservieren möchten oder Autoscaling um die Instanz automatisch zu skalieren. Eine Instanz fungiert als Container für Datenbanken und die Topologie der Datenreplizierung (regional, biregional oder multiregional) wird auf Instanzebene ausgewählt. |
Schlüsselraum |
Datenbank Ein Cassandra-Schlüsselraum entspricht einem Spanner- database, d. h. eine Sammlung von Tabellen und anderen Schemaelementen (z. B. Indexe und Rollen). Im Gegensatz zu einem Schlüsselraum müssen Sie den Replikationsfaktor nicht konfigurieren. Spanner repliziert Ihre Daten automatisch in der in Ihrer Instanz angegebenen Region. |
Tabelle |
Tabelle Sowohl in Cassandra als auch in Spanner sind Tabellen eine Sammlung von Zeilen, die durch einen im Tabellenschema angegebenen Primärschlüssel identifiziert werden. |
Partition |
Aufteilen Sowohl Cassandra als auch Spanner skalieren durch das Sharding von Daten. In Cassandra wird jedes Shard als Partition bezeichnet, in Spanner hingegen wird als Aufteilung bezeichnet. Cassandra verwendet die Hash-Partitionierung. Das bedeutet, dass jede Zeile basierend auf einem Hash des Primärschlüssels unabhängig einem Speicherknoten zugewiesen wird. Spanner ist nach Bereichsshards organisiert. Das bedeutet, dass Zeilen, die im Primärschlüsselbereich zusammenhängen, auch im Speicher zusammenhängen (außer an den Grenzwerten der Splits). Spanner übernimmt die Aufteilung und Zusammenführung basierend auf Last und Speicherplatz. Dies ist für die Anwendung transparent. Der Schlüssel Im Gegensatz zu Cassandra führen Bereichsscans über ein Präfix der primären Schlüssel ist ein effizienter Vorgang in Spanner. |
Zeile |
Zeile Sowohl in Cassandra als auch in Spanner ist eine Zeile eine Sammlung von Spalten durch einen Primärschlüssel eindeutig identifiziert werden. Wie Cassandra, Spanner unterstützt zusammengesetzte Primärschlüssel. Im Gegensatz zu Cassandra unterscheidet Spanner nicht zwischen Partitions- und Sortierschlüssel, da die Daten nach Bereich gruppiert werden. Bei Spanner sind nur Sortierschlüssel, wobei die Partitionierung im Hintergrund verwaltet wird. |
Spalte |
Spalte Sowohl in Cassandra als auch in Spanner ist eine Spalte ein Dataset Werte desselben Typs haben. Für jede Zeile einer Tabelle gibt es einen Wert. Weitere Informationen zum Vergleichen von Cassandra-Spaltentypen mit Spanner finden Sie unter Datentypen. |
Architektur
Ein Cassandra-Cluster besteht aus einer Reihe von Servern und Speicher, die sich zusammen mit für diese Server. Eine Hash-Funktion ordnet Zeilen aus einem Partitionsschlüsselbereich einem virtuellen Knoten (VNode) zu. Jedem Server wird dann nach dem Zufallsprinzip eine Reihe von VNodes zugewiesen, um einen Teil des Clusterschlüsselbereichs bereitzustellen. Der Speicher für die VNodes ist lokal an den Bereitstellungsknoten angehängt. Client-Treiber stellen eine direkte Verbindung zu den Auslieferungsknoten her und übernehmen den Lastenausgleich und das Abfragen des Routings.
Eine Spanner-Instanz besteht aus einer Reihe von Servern in Replikationstopologie. Spanner fragmentiert jede Tabelle dynamisch in Zeilenbereiche basierend auf CPU- und Laufwerksnutzung. Shards werden Rechenknoten für die Auslieferung zugewiesen. Daten sind physisch in Colossus gespeichert sind, dem verteilten Dateisystem von Google, getrennt von die Compute-Knoten. Clienttreiber stellen eine Verbindung zum Frontend von Spanner her die Anfragenrouting und Load-Balancing durchführen. Weitere Informationen finden Sie im Whitepaper Lebensdauer von Lese- und Schreibvorgängen in Cloud Spanner.
Im Großen und Ganzen skalieren beide Architekturen, wenn dem zugrunde liegenden Cluster Ressourcen hinzugefügt werden. Die Trennung von Computing und Speicher bei Spanner ermöglicht ein schnelleres Neuausgleichen der Last zwischen Compute-Knoten als Reaktion auf Änderungen der Arbeitslast. Im Gegensatz zu Cassandra werden bei Shard-Verschiebungen keine Daten verschoben, da die Daten weiter auf einer Seite bleiben. Colossus. Außerdem ist die bereichsbasierte Partitionierung von Spanner für Anwendungen, bei denen Daten nach Partitionsschlüssel sortiert werden sollen, möglicherweise natürlicher. Die Kehrseite der bereichsbasierte Partitionierung ist, dass Arbeitslasten, die in ein Ende des Schlüsselbereichs schreiben, (z. B. Tabellen mit dem aktuellen Zeitstempel) kann es zu einem Heißlaufen kommen, ohne dass zusätzliche Schemadesign berücksichtigen. Weitere Informationen zu Methoden zur Behebung von Hotspots finden Sie unter Best Practices für das Schemadesign.
Konsistenz
Bei Cassandra müssen Sie für jeden Vorgang ein Konsistenzniveau angeben. Wenn Sie die Konsistenzebene „Quorum“ verwenden, muss eine Mehrheit der Replikatknoten auf den Koordinatorknoten antworten, damit der Vorgang als erfolgreich betrachtet wird. Wenn Sie die Konsistenzstufe 1 verwenden, Cassandra benötigt einen einzelnen Replikatknoten um zu antworten, damit der Vorgang als erfolgreich gilt.
Spanner bietet eine hohe Konsistenz. Spanner Die API stellt dem Client keine Replikate zur Verfügung. Die Spanner-Clients interagieren mit Spanner, als wäre es eine Datenbank auf einem einzelnen Computer. Ein Schreibvorgang wird immer in die Mehrheit der Replikate geschrieben, bevor er dem Nutzer bestätigt wird. Alle nachfolgenden Lesevorgänge spiegeln die neu geschriebenen Daten wider. Anwendungen können einen Snapshot der Datenbank zu einem bestimmten Zeitpunkt in der Vergangenheit lesen. Dies kann Leistungsvorteile gegenüber starken Lesevorgängen bieten. Weitere Informationen zur Konsistenz Informationen zu den Attributen von Spanner finden Sie in der Übersicht zu Transaktionen.
Spanner wurde entwickelt, um die Konsistenz und Verfügbarkeit zu unterstützen, die für Anwendungen im großen Maßstab erforderlich sind. Spanner bietet eine hohe Konsistenz in großem Maßstab und mit hoher Leistung. Für Anwendungsfälle, bei denen dies erforderlich ist, unterstützt Spanner Snapshot-Lesungen, bei denen die Anforderungen an die Aktualität gelockert werden.
Datenmodellierung
In diesem Abschnitt werden Cassandra- und Spanner-Datenmodelle verglichen.
Tabellendeklaration
Die Syntax der Tabellendeklaration ist in Cassandra und Spanner ziemlich ähnlich. Sie geben den Tabellennamen, die Spaltennamen und -typen sowie den Primärschlüssel, der eine Zeile eindeutig identifiziert. Der Hauptunterschied besteht darin, dass Cassandra hash-partitioniert ist und zwischen Partitions- und Sortierschlüssel unterscheidet, während Spanner bereichspartitioniert ist. Sie können sich Spanner als eine Art Sortierschlüssel vorstellen, automatisch im Hintergrund verwaltet werden. Wie Cassandra unterstützt Spanner zusammengesetzte Primärschlüssel.
Einzelner Primärschlüsselteil
Der Unterschied zwischen Cassandra und Spanner besteht in den Typennamen und der Position der Primärschlüsselklausel.
Cassandra | Spanner |
---|---|
CREATE TABLE users ( user_id bigint, first_name text, last_name text, PRIMARY KEY (user_id) ) |
CREATE TABLE users ( user_id int64, first_name string(max), last_name string(max), ) PRIMARY KEY (user_id) |
Mehrere Primärschlüsselteile
Bei Cassandra ist der erste Teil des Primärschlüssels der „Partitionsschlüssel“ und die nachfolgenden Teile des Primärschlüssels sind „Sortierschlüssel“. Für Spanner gibt es keinen separaten Partitionsschlüssel. Daten werden nach dem gesamten zusammengesetzten Element sortiert gespeichert Primärschlüssel.
Cassandra | Spanner |
---|---|
CREATE TABLE user_items ( user_id bigint, item_id bigint, first_name text, last_name text, PRIMARY KEY (user_id, item_id) ) |
CREATE TABLE user_items ( user_id int64, item_id int64, first_name string(max), last_name string(max), ) PRIMARY KEY (user_id, item_id) |
Zusammengesetzter Partitionsschlüssel
Bei Cassandra können Partitionierungsschlüssel zusammengesetzte Schlüssel sein. In Spanner gibt es keinen separaten Partitionsschlüssel. Daten werden nach dem gesamten zusammengesetzten Element sortiert gespeichert Primärschlüssel.
Cassandra | Spanner |
---|---|
CREATE TABLE user_category_items ( user_id bigint, category_id bigint, item_id bigint, first_name text, last_name text, PRIMARY KEY ((user_id, category_id), item_id) ) |
CREATE TABLE user_category_items ( user_id int64, category_id int64, item_id int64, first_name string(max), last_name string(max), ) PRIMARY KEY (user_id, category_id, item_id) |
Datentypen
In diesem Abschnitt werden die Datentypen von Cassandra und Spanner verglichen. Weitere Informationen zu Spanner-Typen finden Sie unter Datentypen in GoogleSQL.
Cassandra | Spanner | |
---|---|---|
Numerische Typen |
Standard-Ganzzahlen:bigint (vorzeichenbehaftete 64-Bit-Ganzzahl)int (32-Bit-Ganzzahl mit Vorzeichen)smallint (16-Bit-Ganzzahl mit Vorzeichen)tinyint (vorzeichenbehaftete 8-Bit-Ganzzahl)
|
int64 (64‑Bit-Ganzzahl mit Vorzeichen)Spanner unterstützt einen einzelnen 64‑Bit-Datentyp für Ganzzahlen mit Vorzeichen. |
Standard-Gleitkomma:double (64-Bit-IEEE-754-Gleitkomma)float (32-Bit-IEEE-754-Gleitkomma) |
float64 (64-Bit-IEEE-754-Gleitkomma)float32 (32-Bit-IEEE-754-Gleitkomma)
|
|
Zahlen mit variabler Genauigkeit:varint (ganze Zahl mit variabler Genauigkeit)decimal (Dezimalzahl mit variabler Genauigkeit)
|
Verwenden Sie für Dezimalzahlen mit fester Genauigkeit numeric (Genauigkeit 38, Skalierung 9).
Andernfalls verwenden Sie string in Verbindung mit einer Ganzzahlbibliothek mit variabler Genauigkeit der Anwendungsebene.
|
|
Stringtypen |
text varchar
|
string(max) Sowohl text als auch varchar speichern und validieren UTF-8-Strings. In Spanner muss für string -Spalten die maximale Länge angegeben werden. Dies hat keine Auswirkungen auf die Speicherung, dient aber zu Validierungszwecken.
|
blob |
bytes(max) Verwenden Sie den Datentyp bytes , um Binärdaten zu speichern.
|
|
Datums- und Uhrzeittypen | date |
date |
duration |
int64 In Spanner gibt es keinen speziellen Datentyp für die Dauer. Verwenden Sie int64 , um die Dauer in Nanosekunden zu speichern.
|
|
time |
int64 In Spanner gibt es keinen speziellen Datentyp für die Tageszeit. int64 für Folgendes verwenden:
Nanosekunden-Abweichung
innerhalb eines Tages speichern.
|
|
timestamp |
timestamp |
|
Containertypen | Benutzerdefinierte Typen | json oder proto |
list |
array Mit array können Sie eine Liste von getippten Objekten speichern.
|
|
map |
json oder proto Spanner unterstützt keinen dedizierten Kartentyp. json oder proto verwenden
Spalten zur Darstellung von Karten. Weitere Informationen finden Sie unter Große Karten als verschachtelte Tabellen speichern.
|
|
set |
array In Spanner wird kein spezieller Satztyp unterstützt. Verwenden Sie array -Spalten, um
Eine set , wobei die Eindeutigkeit des Datasets durch die Anwendung verwaltet wird. Weitere Informationen finden Sie unter Große Karten als verschachtelte Tabellen speichern. Diese Methode kann auch zum Speichern großer Datensätze verwendet werden.
|
Grundlegende Nutzungsmuster
Die folgenden Codebeispiele zeigen den Unterschied zwischen Cassandra- und Spanner-Clientcode in Go. Weitere Informationen Siehe Spanner-Clientbibliotheken.
Clientinitialisierung
In Cassandra-Clients erstellen Sie ein Clusterobjekt, das den zugrunde liegenden Cassandra-Cluster darstellt, ein Sitzungsobjekt instanziieren, das eine Verbindung zum Cluster abstrahiert, und Abfragen an die Sitzung senden. In Spanner Ein an eine bestimmte Datenbank gebundenes Clientobjekt erstellen und Datenbankanfragen ausgeben auf dem Client-Objekt.
Beispiel für Cassandra
Go
import "github.com/gocql/gocql" ... cluster := gocql.NewCluster("<address>") cluster.Keyspace = "<keyspace>" session, err := cluster.CreateSession() if err != nil { return err } defer session.Close() // session.Query(...)
Spanner-Beispiel
Go
import "cloud.google.com/go/spanner" ... client, err := spanner.NewClient(ctx, fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, database)) defer client.Close() // client.Apply(...)
Daten lesen
Lesevorgänge in Spanner können sowohl über eine API für Schlüssel/Wert-Paare als auch über eine Abfrage-API ausgeführt werden. Als Cassandra-Nutzer ist Ihnen die Query API vielleicht vertrauter. Ein wichtiger Unterschied bei der Abfrage-API besteht darin, dass in Spanner benannte Argumente erforderlich sind, im Gegensatz zu Positionsargumenten ?
in Cassandra. Der Name eines Arguments in einer Spanner-Abfrage muss mit einem @
beginnen.
Beispiel für Cassandra
Go
stmt := `SELECT user_id, first_name, last_name FROM users WHERE user_id = ?` var ( userID int firstName string lastName string ) err := session.Query(stmt, 1).Scan(&userID, &firstName, &lastName)
Spanner-Beispiel
Go
stmt := spanner.Statement{ SQL: `SELECT user_id, first_name, last_name FROM users WHERE user_id = @user_id`, Params: map[string]any{"user_id": 1}, } var ( userID int64 firstName string lastName string ) err := client.Single().Query(ctx, stmt).Do(func(row *spanner.Row) error { return row.Columns(&userID, &firstName, &lastName) })
Daten einfügen
Cassandra-INSERT
entspricht einem Spanner-INSERT OR UPDATE
.
Sie müssen für eine Einfügung den vollständigen Primärschlüssel angeben. Spanner
unterstützt sowohl DML als auch eine API zur Mutation für Schlüssel/Wert-Paare. Der Stil des Schlüssel/Wert-Paars
Mutation API wird aufgrund der geringeren Latenz für einfache Schreibvorgänge empfohlen.
Die Spanner DML API bietet mehr Funktionen, da sie die gesamte SQL-Oberfläche unterstützt, einschließlich der Verwendung von Ausdrücken in der DML-Anweisung.
Cassandra-Beispiel
Go
stmt := `INSERT INTO users (user_id, first_name, last_name) VALUES (?, ?, ?)` err := session.Query(stmt, 1, "John", "Doe").Exec()
Spanner-Beispiel
Go
_, err := client.Apply(ctx, []*spanner.Mutation{ spanner.InsertOrUpdateMap( "users", map[string]any{ "user_id": 1, "first_name": "John", "last_name": "Doe", } )})
Daten im Batch einfügen
In Cassandra können Sie mithilfe einer Batch-Anweisung mehrere Zeilen einfügen. In Spanner kann ein Commitvorgang mehrere Mutationen enthalten. Spanner fügt diese Mutationen in kleinstmöglichen Schritten in die Datenbank ein.
Beispiel für Cassandra
Go
stmt := `INSERT INTO users (user_id, first_name, last_name) VALUES (?, ?, ?)` b := session.NewBatch(gocql.UnloggedBatch) b.Entries = []gocql.BatchEntry{ {Stmt: stmt, Args: []any{1, "John", "Doe"}}, {Stmt: stmt, Args: []any{2, "Mary", "Poppins"}}, } err = session.ExecuteBatch(b)
Spanner-Beispiel
Go
_, err := client.Apply(ctx, []*spanner.Mutation{ spanner.InsertOrUpdateMap( "users", map[string]any{ "user_id": 1, "first_name": "John", "last_name": "Doe" }, ), spanner.InsertOrUpdateMap( "users", map[string]any{ "user_id": 2, "first_name": "Mary", "last_name": "Poppins", }, ), })
Daten löschen
Für Cassandra-Löschvorgänge müssen Sie den Primärschlüssel der zu löschenden Zeilen angeben.
Dies entspricht der DELETE
-Mutation in Spanner.
Beispiel für Cassandra
Go
stmt := `DELETE FROM users WHERE user_id = ?` err := session.Query(stmt, 1).Exec()
Spanner-Beispiel
Go
_, err := client.Apply(ctx, []*spanner.Mutation{ spanner.Delete("users", spanner.Key{1}), })
Themen für Fortgeschrittene
In diesem Abschnitt erfahren Sie, wie Sie erweiterte Cassandra-Funktionen in Spanner verwenden.
Schreibzeitstempel
In Cassandra können Sie mit der Klausel USING TIMESTAMP
einen Schreibzeitstempel für eine bestimmte Zelle explizit angeben. Normalerweise wird diese Funktion verwendet, um die „Letzter-Schreiber-gewinnt“-Semantik von Cassandra zu manipulieren.
Spanner lässt nicht zu, dass Clients für jedes Element den Zeitstempel angeben
Schreiben. Jede Zelle wird intern mit dem Zeitstempel TrueTime zum Zeitpunkt der Commit-Aktion des Zellenwerts gekennzeichnet. Da Spanner eine starke
konsistente und streng serialisierbare Schnittstelle zur Verfügung steht, benötigen die meisten Anwendungen
von USING TIMESTAMP
.
Wenn Sie für die anwendungsspezifische Logik auf die USING TIMESTAMP
-Spalte von Cassandra zurückgreifen, können Sie Ihrem Spanner-Schema eine zusätzliche USING TIMESTAMP
-Spalte hinzufügen, mit der die Änderungszeit auf Anwendungsebene erfasst werden kann. Aktualisierungen einer Zeile können dann in einen Lese-/Schreibmodus umschlossen werden.
Transaktion. Beispiel:
Cassandra-Beispiel
Go
stmt := `INSERT INTO users (user_id, first_name, last_name) VALUES (?, ?, ?) USING TIMESTAMP ?` err := session.Query(stmt, 1, "John", "Doe", ts).Exec()
Spanner-Beispiel
Erstellen Sie ein Schema mit einer expliziten Spalte für den Zeitstempel der Aktualisierung.
GoogleSQL
CREATE TABLE users ( user_id INT64, first_name STRING(MAX), last_name STRING(MAX), update_ts TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true), ) PRIMARY KEY (user_id)
Passen Sie die Logik an, um die Zeile zu aktualisieren und einen Zeitstempel hinzuzufügen.
Go
func ShouldUpdateRow(ctx context.Context, txn *spanner.ReadWriteTransaction, updateTs time.Time) (bool, error) { // Read the existing commit timestamp. row, err := txn.ReadRow(ctx, "users", spanner.Key{1}, []string{"update_ts"}) // Treat non-existent row as NULL timestamp - the row should be updated. if spanner.ErrCode(err) == codes.NotFound { return true, nil } // Propagate unexpected errors. if err != nil { return false, err } // Check if the committed timestamp is newer than the update timestamp. var committedTs *time.Time err = row.Columns(&committedTs) if err != nil { return false, err } if committedTs != nil && committedTs.Before(updateTs) { return false, nil } // Committed timestamp is older than update timestamp - the row should be updated. return true, nil }
Prüfen Sie die benutzerdefinierte Bedingung, bevor Sie die Zeile aktualisieren.
Go
_, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { // Check if the row should be updated. ok, err := ShouldUpdateRow(ctx, txn, time.Now()) if err != nil { return err } if !ok { return nil } // Update the row. txn.BufferWrite([]*spanner.Mutation{ spanner.InsertOrUpdateMap("users", map[string]any{ "user_id": 1, "first_name": "John", "last_name": "Doe", "update_ts": spanner.CommitTimestamp, })}) return nil })
Bedingte Mutationen
Die INSERT ... IF EXISTS
-Anweisung in Cassandra entspricht der Anweisung INSERT
.
in Spanner erstellen. In beiden Fällen schlägt das Einfügen fehl, wenn die Zeile bereits vorhanden ist.
In Cassandra können Sie auch DML-Anweisungen erstellen, die eine Bedingung angeben.
die Anweisung schlägt fehl, wenn die Bedingung als falsch ausgewertet wird. In
Spanner haben, können Sie das bedingte UPDATE
verwenden.
Mutationen in Lese-Schreib-Transaktionen. So können Sie beispielsweise eine Zeile nur aktualisieren, wenn eine bestimmte Bedingung erfüllt ist:
Cassandra-Beispiel
Go
stmt := `UPDATE users SET last_name = ? WHERE user_id = ? IF first_name = ?` err := session.Query(stmt, 1, "Smith", "John").Exec()
Spanner-Beispiel
Passen Sie die Logik so an, dass die Zeile aktualisiert und eine Bedingung eingefügt wird.
Go
func ShouldUpdateRow(ctx context.Context, txn *spanner.ReadWriteTransaction) (bool, error) { row, err := txn.ReadRow(ctx, "users", spanner.Key{1}, []string{"first_name"}) if err != nil { return false, err } var firstName *string err = row.Columns(&firstName) if err != nil { return false, err } if firstName != nil && firstName == "John" { return false, nil } return true, nil }
Prüfen Sie die benutzerdefinierte Bedingung, bevor Sie die Zeile aktualisieren.
Go
_, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { ok, err := ShouldUpdateRow(ctx, txn, time.Now()) if err != nil { return err } if !ok { return nil } txn.BufferWrite([]*spanner.Mutation{ spanner.InsertOrUpdateMap("users", map[string]any{ "user_id": 1, "last_name": "Smith", "update_ts": spanner.CommitTimestamp, })}) return nil })
TTL
Cassandra unterstützt das Festlegen eines TTL-Werts (Time to Live) auf Zeilen- oder Spaltenebene. In Spanner wird die TTL auf Zeilenebene konfiguriert und Sie legen einen benannten als Ablaufzeit für die Zeile ein. Weitere Informationen finden Sie in der Übersicht über die Gültigkeitsdauer (TTL):
Beispiel für Cassandra
Go
stmt := `INSERT INTO users (user_id, first_name, last_name) VALUES (?, ?, ?) USING TTL 86400 ?` err := session.Query(stmt, 1, "John", "Doe", ts).Exec()
Spanner-Beispiel
Schema mit einer expliziten Spalte mit Zeitstempel der Aktualisierung erstellen
GoogleSQL
CREATE TABLE users ( user_id INT64, first_name STRING(MAX), last_name STRING(MAX), update_ts TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true), ) PRIMARY KEY (user_id), ROW DELETION POLICY (OLDER_THAN(update_ts, INTERVAL 1 DAY));
Fügen Sie Zeilen mit einem Commit-Zeitstempel ein.
Go
_, err := client.Apply(ctx, []*spanner.Mutation{ spanner.InsertOrUpdateMap("users", map[string]any{ "user_id": 1, "first_name": "John", "last_name": "Doe", "update_ts": spanner.CommitTimestamp}), })
Große Karten als verschränkte Tabellen speichern.
Cassandra unterstützt den Typ map
für das Speichern sortierter Schlüssel/Wert-Paare. Zum Speichern
map
-Typen, die eine kleine Datenmenge in Spanner enthalten,
kann JSON
oder PROTO
verwenden
-Typen, mit denen Sie semistrukturierte bzw. strukturierte Daten speichern können.
Bei Aktualisierungen solcher Spalten muss der gesamte Spaltenwert neu geschrieben werden. Wenn Sie
haben einen Anwendungsfall, bei dem eine große Menge an Daten in einer Cassandra-map
gespeichert ist.
muss nur ein kleiner Teil des map
aktualisiert werden. Dabei wird
INTERLEAVED
Tabellen gut passen. So ordnen Sie beispielsweise einer großen Menge von Schlüssel/Wert-Daten einen bestimmten Nutzer zu:
Cassandra-Beispiel
CREATE TABLE users (
user_id bigint,
attachments map<string, string>,
PRIMARY KEY (user_id)
)
Spanner-Beispiel
CREATE TABLE users (
user_id INT64,
) PRIMARY KEY (user_id);
CREATE TABLE user_attachments (
user_id INT64,
attachment_key STRING(MAX),
attachment_val STRING(MAX),
) PRIMARY KEY (user_id, attachment_key);
In diesem Fall wird die Zeile „Nutzeranhang“ zusammen mit dem entsprechenden Nutzerzeile und kann gemeinsam mit der Nutzerzeile abgerufen und aktualisiert werden. Sie können die Funktion read-write APIs in Spanner für die Interaktion mit verschränkten Tabellen Weitere Informationen zum Interleaving finden Sie unter Übergeordnete und untergeordnete Tabellen erstellen.
Für Entwickler
In diesem Abschnitt werden Spanner und Cassandra verglichen Entwicklertools.
Lokale Entwicklung
Sie können Cassandra lokal für die Entwicklung und für Unit-Tests ausführen. Spanner bietet über den Spanner-Emulator eine ähnliche Umgebung für die lokale Entwicklung. Der Emulator bietet eine hohe Fidelity-Umgebung für interaktive Entwicklung und Einheitentests. Weitere Informationen Weitere Informationen finden Sie unter Spanner lokal emulieren.
Befehlszeile
Das Spanner-Äquivalent zu Cassandras nodetool
ist die Google Cloud CLI. Mit gcloud spanner
können Sie Steuerungsebenen- und Datenebenen-Vorgänge ausführen. Weitere Informationen finden Sie in der
Referenzhandbuch für Google Cloud CLI Spanner
Wenn Sie eine REPL-Schnittstelle benötigen, um Abfragen an Spanner zu senden
ähnlich wie cqlsh
können Sie das spanner-cli
-Tool verwenden. Installation und Ausführung
spanner-cli
in Go:
go install github.com/cloudspannerecosystem/spanner-cli@latest
$(go env GOPATH)/bin/spanner-cli
Weitere Informationen finden Sie im GitHub-Repository für spanner-cli.