Spanner per gli utenti Cassandra

Questo documento mette a confronto i concetti e le pratiche di Apache Cassandra e Spanner. Si presume che tu abbia familiarità con Cassandra e che voglia eseguire la migrazione di applicazioni esistenti o progettarne di nuove utilizzando Spanner come database.

Cassandra e Spanner sono entrambi database distribuiti su larga scala progettati per applicazioni che richiedono elevata scalabilità e bassa latenza. Sebbene entrambi i database possano supportare carichi di lavoro NoSQL impegnativi, Spanner offre funzionalità avanzate per la definizione del modello di dati, le query e le operazioni transactional. Per saperne di più su come Spanner soddisfa i criteri dei database NoSQL, consulta Spanner per i carichi di lavoro non relazionali.

Esegui la migrazione da Cassandra a Spanner

Per eseguire la migrazione da Cassandra a Spanner, puoi utilizzare l'adattatore proxy Cassandra to Spanner. Questo strumento open source consente di migrare i carichi di lavoro da Cassandra o DataStax Enterprise (DSE) a Spanner senza modifiche alla logica dell'applicazione.

Concetti principali

Questa sezione confronta i concetti chiave di Cassandra e Spanner.

Terminologia

Cassandra Spanner
Cluster Istanza

Un cluster Cassandra è equivalente a un'istanza Spanner, ovvero una raccolta di server e risorse di archiviazione. Poiché Spanner è un servizio gestito, non devi configurare l'hardware o il software di base. Devi solo specificare la quantità di nodi da prenotare per l'istanza o scegliere la scalabilità automatica per eseguire lo scaling automatico dell'istanza. Un'istanza agisce come un contenitore per i database e la topologia di replica dei dati (regionale, a due regioni o multiregionale) viene scelta a livello di istanza.
Spazio delle chiavi Database

Uno spazio delle chiavi Cassandra è equivalente a uno Spanner database, una raccolta di tabelle e altri elementi dello schema (ad esempio indici e ruoli). A differenza di uno spazio chiavi, non è necessario configurare il fattore di replica. Spanner replica automaticamente i dati nella regione designata nell'istanza.
Tabella Tabella

Sia in Cassandra che in Spanner, le tabelle sono una raccolta righe identificate da una chiave primaria specificata nello schema della tabella.
Partizione Dividi

Sia Cassandra che Spanner scalano mediante lo sharding dei dati. In Cassandra, ogni shard viene chiamato partizione, mentre in Spanner ogni shard è chiamato suddivisione. Cassandra utilizza il partizionamento hash, il che significa che ogni riga viene assegnata in modo indipendente a un nodo di archiviazione in base a un hash della chiave principale. Spanner è suddiviso in intervalli, il che significa che le righe che sono contigui nello spazio della chiave primaria sono contigui anche nello spazio di archiviazione (tranne nei confini della suddivisione). Spanner si occupa di suddividere e unire le parti in base al carico e allo spazio di archiviazione, in modo trasparente per l'applicazione. La chiave è che, a differenza di Cassandra, le scansioni dell'intervallo su un prefisso è un'operazione efficiente in Spanner.
Riga Riga

Sia in Cassandra che in Spanner, una riga è una raccolta di colonne identificati in modo univoco da una chiave primaria. Come Cassandra, Spanner supporta chiavi primarie composte. A differenza di Cassandra, Spanner non fa distinzione tra chiave di partizione e chiave di ordinamento, perché i dati sono suddivisi in partizioni in base all'intervallo. Si può pensare a Spanner come a un database che ha solo chiavi di ordinamento, con il partizionamento gestito in background.
Colonna Colonna

Sia in Cassandra che in Spanner, una colonna è un insieme di dati che presentano lo stesso tipo. Esiste un valore per ogni riga di una tabella. Per ulteriori informazioni sul confronto dei tipi di colonna Cassandra con Spanner, vedi Tipi di dati.

Architettura

Un cluster Cassandra è costituito da un insieme di server e spazio di archiviazione collocati in su quei server. Una funzione hash mappa le righe dallo spazio di una chiave di partizione a uno spazio Node (vnode). A ogni server viene quindi assegnato in modo casuale un insieme di vnode per gestire una parte dello spazio delle chiavi del cluster. Lo spazio di archiviazione per i vnodi è collegato localmente il nodo di pubblicazione. I driver client si connettono direttamente ai nodi di gestione per gestire il bilanciamento del carico e il routing delle query.

Un'istanza Spanner è costituita da un insieme di server in una topologia di replica. Spanner esegue lo sharding dinamico di ogni tabella in intervalli di righe in base a Utilizzo di CPU e disco. Gli shard vengono assegnati ai nodi di computing per la pubblicazione. I dati sono archiviati fisicamente su Colossus, il file system distribuito di Google, dei nodi di computing. I driver client si connettono ai server frontend di Spanner che eseguono il routing delle richieste e il bilanciamento del carico. Per saperne di più, consulta il whitepaper Durata di letture e scritture di Spanner.

A livello generale, entrambe le architetture scalano man mano che le risorse vengono aggiunte del cluster sottostante. La separazione di computing e archiviazione di Spanner consente più ribilanciamento del carico tra i nodi di computing in risposta alle modifiche dei carichi di lavoro. A differenza di Cassandra, i trasferimenti di shard non comportano trasferimenti di dati, poiché i dati rimangono su Colossus. Inoltre, il partizionamento basato sull'intervallo di Spanner potrebbe essere più naturale applicazioni che si aspettano che i dati vengano ordinati per chiave di partizione. Il rovescio della porta Il partizionamento basato sull'intervallo è che i carichi di lavoro che scrivono in un'estremità dello spazio delle chiavi (ad esempio, le tabelle codificate dal timestamp corrente) potrebbero essere soggette all'hotspotting senza ulteriori progettazione dello schema. Per saperne di più sulle tecniche per superare gli hotspot, consulta le best practice per la progettazione degli schemi.

Coerenza

Con Cassandra, devi specificare un livello di coerenza per ogni operazione. Se utilizzi il livello di coerenza del quorum, una maggioranza dei nodi di replica deve rispondere per far sì che l'operazione venga considerata riuscita. Se utilizzi il livello di coerenza 1, Cassandra ha bisogno di un nodo di replica singola affinché l'operazione venga considerata riuscita.

Spanner offre elevata coerenza. L'API Spanner non espone le repliche al client. I client di Spanner interagiscono Spanner come se fosse un database di una singola macchina. Un'operazione di scrittura viene sempre eseguita su una maggioranza di repliche prima di essere confermata all'utente. Le letture successive riflettono i nuovi dati scritti. Le applicazioni possono di leggere uno snapshot del database in un momento passato, che potrebbe essere in termini di prestazioni rispetto a letture efficaci. Per ulteriori informazioni sulle proprietà di coerenza di Spanner, consulta la Panoramica delle transazioni.

Spanner è stato creato per supportare la coerenza e la disponibilità necessarie nelle applicazioni su larga scala. Spanner offre elevata coerenza su larga scala e con prestazioni elevate. Per i casi d'uso che lo richiedono, Spanner supporta le letture di snapshot che riducono i requisiti di aggiornamento.

Modellazione dei dati

Questa sezione mette a confronto i modelli di dati di Cassandra e Spanner.

Dichiarazione della tabella

La sintassi delle dichiarazioni delle tabelle è abbastanza simile in Cassandra e Spanner. Specifica il nome della tabella, i nomi e i tipi di colonna e la chiave primaria che identifica in modo univoco una riga. La differenza principale è che Cassandra è partizionata con hash e fa una distinzione tra chiave di partizione di ordinamento, mentre Spanner è partizionato per intervallo. Si può pensare che Spanner abbia solo chiavi di ordinamento, con le partizioni vengono mantenute automaticamente in background. Come Cassandra, Spanner supporta le chiavi primarie composite.

Singola parte della chiave primaria

La differenza tra Cassandra e Spanner sta nei nomi dei tipi e la posizione della clausola di chiave primaria.

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)
    

Più parti della chiave primaria

Per Cassandra, la prima parte della chiave primaria è la "chiave di partizione" e ai le parti successive della chiave primaria sono le "chiavi di ordinamento". Per Spanner, non esiste una chiave di partizione separata. I dati vengono archiviati ordinati in base all'intero elemento composito e la chiave primaria.

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)
    

Chiave di partizione composita

Per Cassandra, le chiavi di partizione possono essere composte. In Spanner non è presente una chiave di partizione distinta. I dati vengono archiviati in ordine per l'intera chiave primaria composita.

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)
    

Tipi di dati

Questa sezione confronta i tipi di dati di Cassandra e Spanner. Per per saperne di più sui tipi di Spanner, Tipi di dati in GoogleSQL.

Cassandra Spanner
Tipi numerici Numeri interi standard:

bigint (numero intero a 64 bit firmato)
int (numero intero a 32 bit firmato)
smallint (numero intero a 16 bit firmato)
tinyint (numero intero a 8 bit firmato)
int64 (numero intero con segno a 64 bit)

Spanner supporta un singolo tipo di dati a 64 bit per i numeri interi firmati.
Virgola mobile standard:

double (valore in virgola mobile IEEE-754 a 64 bit)
float (rappresentazione in virgola mobile IEEE-754 a 32 bit)
float64 (virgola mobile IEEE-754 a 64 bit)
float32 (virgola mobile IEEE-754 a 32 bit)
Numeri di precisione variabili:

varint (numero intero a precisione variabile)
decimal (decimale con precisione variabile)
Per i numeri decimali con precisione fissa, utilizza numeric (precisione 38, scala 9). In caso contrario, utilizza string insieme a una variabile di livello dell'applicazione libreria di numeri interi di precisione.
Tipi di stringhe text
varchar
string(max)

Sia text sia varchar memorizzano e convalidano le stringhe UTF-8. In Spanner, le colonne string devono specificare la loro lunghezza massima (non influisce sullo spazio di archiviazione; è per motivi di convalida).
blob bytes(max)

Per archiviare dati binari, utilizza il tipo di dati bytes.
Tipi di data e ora date date
duration int64

Spanner non supporta un tipo di dati relativi alla durata dedicata. Utilizza int64 per memorizzare la durata in nanosecondi.
time int64

Spanner non supporta un tipo di dati dedicato per l'ora del giorno. Utilizza int64 per memorizzare l'offset in nanosecondi all'interno di un giorno.
timestamp timestamp
Tipi di contenitori Tipi definiti dall'utente json o proto
list array

Utilizza array per archiviare un elenco di oggetti digitati.
map json o proto

Spanner non supporta un tipo di mappa dedicato. Utilizza le colonne json o proto per rappresentare le mappe. Per ulteriori informazioni, vedi Archiviare mappe di grandi dimensioni come tabelle con interleaving.
set array

Spanner non supporta un tipo di set dedicato. Utilizza le colonne array per rappresentare un set, con l'applicazione che gestisce l'univocità del set. Per ulteriori informazioni, consulta Archiviare mappe di grandi dimensioni come tabelle interlacciate, che possono essere utilizzate anche per archiviare set di grandi dimensioni.

Pattern di utilizzo di base

I seguenti esempi di codice mostrano la differenza tra il codice client Cassandra e il codice client Spanner in Go. Per ulteriori informazioni, consulta Librerie client di Spanner.

Inizializzazione client

Nei client Cassandra, crei un oggetto cluster che rappresenta il cluster Cassandra sottostante, esegui l'inizializzazione di un oggetto sessione che astrae una connessione al cluster ed esegui query sulla sessione. In Spanner, crei un oggetto client associato a un database specifico ed emetti richieste di database sull'oggetto client.

Esempio di Cassandra

Vai

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(...)

Esempio di Spanner

Vai

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(...)

Lettura di dati

Le letture in Spanner possono essere eseguite sia tramite un'API in stile chiave-valore sia tramite un'API di query. Come utente di Cassandra, potresti trovare più familiare l'API Query. Una differenza fondamentale nell'API di query è che Spanner richiede argomenti denominati (a differenza degli argomenti posizionali ? in Cassandra). Il nome di un in una query Spanner deve essere preceduto da un @.

Esempio di Cassandra

Vai

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)

Esempio di Spanner

Vai

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)
})

Inserisci i dati

Un INSERT di Cassandra è equivalente a un INSERT OR UPDATE di Spanner. Devi specificare la chiave primaria completa per un inserimento. Chiave inglese supporta sia DML sia un'API di mutazione dello stile delle coppie chiave-valore. Lo stile della coppia chiave-valore l'API Mutazione è consigliata per le scritture di poco conto grazie alla minore latenza. L'API Spanner DML offre più funzionalità in quanto supporta l'intera piattaforma SQL (incluso l'uso di espressioni nell'istruzione DML).

Esempio di Cassandra

Vai

stmt := `INSERT INTO
           users (user_id, first_name, last_name)
         VALUES
           (?, ?, ?)`
err := session.Query(stmt, 1, "John", "Doe").Exec()

Esempio di Spanner

Vai

_, err := client.Apply(ctx, []*spanner.Mutation{
  spanner.InsertOrUpdateMap(
    "users", map[string]any{
      "user_id":    1,
      "first_name": "John",
      "last_name":  "Doe",
    }
  )})

Inserire i dati in batch

In Cassandra, puoi inserire più righe utilizzando un'istruzione batch. In Spanner, un'operazione di commit può contenere più mutazioni. Spanner inserisce queste mutazioni nel database in modo atomico.

Esempio di Cassandra

Vai

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)

Esempio di Spanner

Vai

_, 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",
    },
  ),
})

Elimina dati

Le eliminazioni Cassandra richiedono di specificare la chiave primaria delle righe da eliminare. È simile alla mutazione DELETE in Spanner.

Esempio di Cassandra

Vai

stmt := `DELETE FROM
           users
         WHERE
           user_id = ?`
err := session.Query(stmt, 1).Exec()

Esempio di Spanner

Vai

_, err := client.Apply(ctx, []*spanner.Mutation{
  spanner.Delete("users", spanner.Key{1}),
})

Argomenti avanzati

Questa sezione contiene informazioni su come utilizzare le funzionalità più avanzate di Cassandra in Spanner.

Scrittura timestamp

Cassandra consente alle mutazioni di specificare esplicitamente un timestamp di scrittura per una determinata cella utilizzando la clausola USING TIMESTAMP. In genere, questa funzione è utilizzato per manipolare la semantica dell'ultimo scrittore di Cassandra.

Spanner non consente ai client di specificare il timestamp di ogni e scrivere. Ogni cella è contrassegnata internamente con il timestamp TrueTime al momento dell'commit del valore della cella. Poiché Spanner fornisce un'interfaccia fortemente coerente e rigorosamente serializzabile, la maggior parte delle applicazioni non ha bisogno della funzionalità di USING TIMESTAMP.

Se utilizzi USING TIMESTAMP di Cassandra per la logica specifica dell'applicazione, puoi aggiungere un'altra colonna TIMESTAMP allo schema di Spanner, che può monitorare la data e l'ora di modifica a livello di applicazione. Gli aggiornamenti di una riga possono quindi essere aggregati in operazioni di lettura e scrittura transazione. Ad esempio:

Esempio di Cassandra

Vai

stmt := `INSERT INTO
           users (user_id, first_name, last_name)
         VALUES
           (?, ?, ?)
         USING TIMESTAMP
           ?`
err := session.Query(stmt, 1, "John", "Doe", ts).Exec()

Esempio di Spanner

  1. Crea lo schema con una colonna esplicita del timestamp di aggiornamento.

    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)
  2. Personalizza la logica per aggiornare la riga e includere un timestamp.

    Vai

    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
    }
  3. Controlla la condizione personalizzata prima di aggiornare la riga.

    Vai

    _, 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
    })

Mutazioni condizionali

L'istruzione INSERT ... IF EXISTS in Cassandra è equivalente all'istruzione INSERT in Spanner. In entrambi i casi, l'inserimento non riesce se la riga esiste già.

In Cassandra, puoi anche creare istruzioni DML che specificano una condizione l'istruzione non riesce se la condizione è false. In Spanner, puoi utilizzare le mutazioni UPDATE condizionali nelle transazioni di lettura-scrittura. Ad esempio, per aggiornare una riga solo se esiste una determinata condizione:

Esempio di Cassandra

Vai

stmt := `UPDATE
           users
         SET
           last_name = ?
         WHERE
           user_id = ?
         IF
           first_name = ?`
err := session.Query(stmt, 1, "Smith", "John").Exec()

Esempio di Spanner

  1. Personalizza la logica per aggiornare la riga e includere una condizione.

    Vai

    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
    }
  2. Controlla la condizione personalizzata prima di aggiornare la riga.

    Vai

    _, 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 supporta l'impostazione di un valore TTL (Time to Live) a livello di riga o colonna. In Spanner, il TTL è configurato a livello di riga e designi una chiamata come data di scadenza della riga. Per ulteriori informazioni, consulta Panoramica della durata (TTL).

Esempio di Cassandra

Vai

stmt := `INSERT INTO
           users (user_id, first_name, last_name)
         VALUES
           (?, ?, ?)
         USING TTL 86400
           ?`
err := session.Query(stmt, 1, "John", "Doe", ts).Exec()

Esempio di Spanner

  1. Creare uno schema con una colonna timestamp di aggiornamento esplicita

    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));
  2. Inserisci righe con un timestamp di commit.

    Vai

    _, 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}),
    })

Memorizza mappe di grandi dimensioni come tabelle con interfoliazione.

Cassandra supporta il tipo map per l'archiviazione di coppie chiave/valore ordinate. Per archiviare map di tipi che contengono una piccola quantità di dati in Spanner, puoi utilizzare JSON o PROTO che consentono di archiviare rispettivamente dati semistrutturati e strutturati. Gli aggiornamenti di queste colonne richiedono la riscrittura dell'intero valore della colonna. Se hai un caso d'uso in cui una grande quantità di dati è archiviata in un map Cassandra e solo una piccola parte del map deve essere aggiornata, l'utilizzo delle tabelle INTERLEAVED potrebbe essere una buona soluzione. Ad esempio, per associare una grande quantità di dati chiave-valore a un determinato utente:

Esempio di Cassandra

CREATE TABLE users (
  user_id     bigint,
  attachments map<string, string>,
  PRIMARY KEY (user_id)
)

Esempio di Spanner

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 questo caso, una riga degli allegati dell'utente viene archiviata e collocata insieme riga dell'utente e può essere recuperata e aggiornata in modo efficiente insieme alla riga dell'utente. Puoi utilizzare la funzionalità di lettura/scrittura API in Spanner per interagire con le tabelle con interleaving. Per ulteriori informazioni sull'interlacciamento, consulta Creare tabelle principali e secondarie.

Esperienza di sviluppo

Questa sezione confronta Spanner e Cassandra strumenti per sviluppatori.

Sviluppo locale

Puoi eseguire Cassandra localmente per lo sviluppo e i test di unità. Spanner fornisce un ambiente simile per lo sviluppo locale tramite l'emulatore Spanner. L'emulatore offre un alto di verifica delle immagini per lo sviluppo interattivo e i test delle unità. Per ulteriori informazioni, consulta Emulare Spanner localmente.

Riga di comando

L'equivalente di Spanner al valore nodetool di Cassandra è Google Cloud CLI. Puoi eseguire il piano di controllo e le richieste le operazioni dei piani utilizzando gcloud spanner. Per ulteriori informazioni, consulta la guida di riferimento di Spanner per Google Cloud CLI.

Se hai bisogno di un'interfaccia REPL per eseguire query su Spanner simile a cqlsh, puoi utilizzare lo strumento spanner-cli. Per installare ed eseguire spanner-cli in Go:

go install github.com/cloudspannerecosystem/spanner-cli@latest

$(go env GOPATH)/bin/spanner-cli

Per maggiori informazioni, consulta il repository GitHub Spanner-cli.