Spanner untuk pengguna Cassandra

Dokumen ini membandingkan Apache Cassandra dan Konsep dan praktik Spanner. Panduan ini mengasumsikan bahwa Anda sudah terbiasa dengan Cassandra dan ingin memigrasikan aplikasi yang ada atau mendesain aplikasi baru saat menggunakan Spanner sebagai database Anda.

Cassandra dan Spanner adalah database terdistribusi berskala besar dibangun untuk aplikasi yang membutuhkan skalabilitas tinggi dan latensi rendah. Meskipun keduanya dapat mendukung workload NoSQL yang berat, Spanner menyediakan fitur lanjutan untuk pemodelan data, kueri, dan operasional bisnis. Untuk mengetahui informasi selengkapnya tentang cara Spanner memenuhi kriteria database NoSQL, lihat Spanner untuk workload non-relasional.

Konsep inti

Bagian ini membandingkan konsep utama Cassandra dan Spanner.

Terminologi

Cassandra Spanner
Cluster Instance

Gugus Cassandra setara dengan Spanner instance - kumpulan server dan untuk semua resource penyimpanan. Karena Spanner adalah layanan terkelola, Anda tidak perlu mengkonfigurasi perangkat keras atau perangkat lunak yang mendasarinya. Anda hanya perlu menentukan jumlah node yang ingin dicadangkan untuk instance atau memilih penskalaan otomatis untuk menskalakan instance secara otomatis. Instance berfungsi seperti container untuk database, dan topologi replikasi data (regional, dual-region, atau multi-region) dipilih di level instance.
Keyspace Database

Keyspace Cassandra setara dengan Spanner database, yang merupakan kumpulan tabel dan elemen skema lainnya (misalnya, indeks dan peran). Tidak seperti keyspace, Anda tidak perlu mengkonfigurasi faktor replikasi. Spanner otomatis mereplikasi data Anda ke region yang ditentukan dalam di instance Compute Engine.
Tabel Tabel

Di Cassandra dan Spanner, tabel adalah kumpulan baris yang diidentifikasi dengan {i>primary key <i}yang ditentukan di skema tabel.
Partisi Pisahkan

Cassandra dan Spanner menskalakan dengan sharding data. Di Cassandra, setiap shard disebut partisi, sedangkan di Spanner, setiap shard disebut {i>split<i}. {i>Cassandra<i} menggunakan partisi {i>hash<i}, yang berarti bahwa setiap baris ditetapkan secara independen ke node penyimpanan berdasarkan {i>hash <i}dari kunci utama. Spanner dibagi rentangnya, artinya baris yang berdekatan di ruang kunci utama juga berdekatan dalam penyimpanan (kecuali di batas pemisahan). Spanner menangani pemisahan dan penggabungan berdasarkan beban dan penyimpanan, dan ini transparan pada aplikasi. Implikasi utama adalah bahwa tidak seperti Cassandra, pemindaian rentang pada awalan kunci utama adalah operasi yang efisien di Spanner.
Baris Baris

Di Cassandra dan Spanner, baris adalah kumpulan kolom diidentifikasi secara unik dengan {i>primary key<i}. Seperti Cassandra, Spanner mendukung kunci utama gabungan. Tidak seperti Cassandra, Spanner tidak membedakan antara kunci partisi dan kunci sortir, karena data di-sharding rentang. Kita dapat menganggap Spanner hanya memiliki kunci sortir, partisi yang dikelola di balik layar.
Kolom Kolom

Di Cassandra dan Spanner, kolom adalah kumpulan data nilai yang memiliki jenis yang sama. Ada satu nilai untuk setiap baris tabel. Untuk informasi lebih lanjut tentang membandingkan jenis kolom Cassandra dengan Spanner, lihat Jenis Data.

Arsitektur

Cluster Cassandra terdiri dari sekumpulan server dan penyimpanan yang ditempatkan bersama dengan server tersebut. Fungsi {i>hash <i}memetakan baris dari ruang kunci partisi ke ruang {i>node<i} (vnode). Satu set vnode kemudian ditugaskan secara acak ke setiap server untuk melayani sebagian dari ruang kunci cluster. Penyimpanan untuk vnode terpasang secara lokal ke ke node inferensi. {i>Driver<i} klien terhubung langsung ke {i>node <i}layanan dan menangani load balancing dan perutean kueri.

Instance Spanner terdiri dari serangkaian server dalam topologi replikasi. Spanner secara dinamis melakukan sharding setiap tabel ke dalam rentang baris berdasarkan Penggunaan CPU dan disk. Shard ditetapkan ke node komputasi untuk inferensi. Data adalah secara fisik disimpan di Colossus, sistem file terdistribusi milik Google, terpisah dari ke node komputasi. Driver klien terhubung ke frontend Spanner server yang melakukan perutean permintaan dan load balancing. Untuk mempelajari lebih lanjut, lihat Bacaan Spanner & Menulis laporan resmi.

Pada level yang tinggi, kedua arsitektur menskalakan seiring resource ditambahkan ke cluster yang mendasarinya. Pemisahan komputasi dan penyimpanan Spanner memungkinkan penyeimbangan beban yang lebih cepat antara node komputasi sebagai respons terhadap perubahan beban kerja. Tidak seperti Cassandra, pemindahan shard tidak melibatkan pemindahan data karena data tetap berada di Colossus. Selain itu, partisi berbasis rentang Spanner mungkin lebih alami untuk aplikasi yang mengharapkan data diurutkan berdasarkan kunci partisi. Sisi lain dari partisi berbasis rentang adalah beban kerja yang menulis ke salah satu ujung ruang kunci (misalnya, tabel yang diberi kunci berdasarkan stempel waktu saat ini) mungkin mengalami hotspotting tanpa pertimbangan desain skema tambahan. Untuk informasi selengkapnya tentang teknik mengatasi hotspotting, lihat Praktik terbaik desain skema.

Konsistensi

Dengan Cassandra, Anda harus menentukan tingkat konsistensi untuk setiap operasi. Jika Anda menggunakan di tingkat konsistensi kuorum, mayoritas node replika harus merespons {i>node<i} koordinator untuk operasi yang akan dianggap berhasil. Jika Anda menggunakan level konsistensi satu, Cassandra memerlukan satu node replika untuk merespons agar operasi dianggap berhasil.

Spanner memberikan konsistensi yang kuat. Spanner API tidak mengekspos replika ke klien. Klien Spanner berinteraksi dengan Spanner seolah-olah merupakan database mesin tunggal. Tulisan adalah selalu ditulis ke sebagian besar replika sebelum dikonfirmasi kepada pengguna. Setiap pembacaan berikutnya mencerminkan data yang baru ditulis. Aplikasi dapat memilih untuk membaca {i>snapshot<i} dari {i>database<i} pada satu waktu di masa lalu, yang mungkin memiliki manfaat performa dibandingkan pembacaan yang kuat. Untuk informasi selengkapnya tentang konsistensi Properti Spanner, lihat Ringkasan transaksi.

Spanner dibuat untuk mendukung konsistensi dan ketersediaan yang dibutuhkan dalam aplikasi skala besar. Spanner memberikan kemampuan konsistensi dalam skala besar dan dengan performa tinggi. Untuk kasus penggunaan yang memerlukannya, Spanner mendukung pembacaan snapshot yang memenuhi persyaratan kesegaran.

Pemodelan data

Bagian ini membandingkan model data Cassandra dan Spanner.

Deklarasi tabel

Sintaksis deklarasi tabel cukup mirip di Cassandra dan Spanner. Anda menentukan nama tabel, nama dan jenis kolom, dan {i>primary key<i} yang secara unik mengidentifikasi baris. Perbedaan utamanya adalah Cassandra dipartisi hash dan membuat perbedaan antara kunci partisi dan kunci pengurutan, sedangkan Spanner dipartisi rentang. Spanner dapat dianggap sebagai hanya memiliki kunci sortir, dengan partisi secara otomatis dikelola di balik layar. Seperti Cassandra, Spanner mendukung kunci utama gabungan.

Satu bagian kunci utama

Perbedaan antara Cassandra dan Spanner terletak pada nama jenis dan lokasi klausa {i>primary key<i}.

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)
    

Beberapa bagian kunci utama

Untuk Cassandra, bagian kunci utama yang pertama adalah "kunci partisi" dan bagian kunci utama berikutnya adalah "sort key". Untuk Spanner, tidak ada kunci partisi terpisah. Data disimpan dan diurutkan berdasarkan seluruh komposit {i>primary key<i}-nya.

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)
    

Kunci partisi gabungan

Untuk Cassandra, kunci partisi dapat berupa komposit. Tidak ada partisi terpisah di Spanner. Data disimpan dan diurutkan berdasarkan seluruh komposit {i>primary key<i}-nya.

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)
    

Jenis data

Bagian ini membandingkan jenis data Cassandra dan Spanner. Untuk mengetahui informasi selengkapnya tentang jenis Spanner, lihat Jenis data di GoogleSQL.

Cassandra Spanner
Jenis Numerik Bilangan bulat standar:

bigint (bilangan bulat dengan tanda tangan 64-bit)
int (bilangan bulat bertanda 32-bit)
smallint (bilangan bulat dengan tanda tangan 16-bit)
tinyint (bilangan bulat bertanda 8-bit)
int64 (bilangan bulat dengan tanda tangan 64-bit)

Spanner mendukung jenis data lebar 64-bit tunggal untuk bilangan bulat yang ditandatangani.
Floating point standar:

double (floating point IEEE-754 64-bit)
float (floating point IEEE-754 32-bit)
float64 (floating point IEEE-754 64-bit)
float32 (floating point IEEE-754 32-bit)
Angka presisi variabel:

varint (bilangan bulat presisi variabel)
decimal (desimal presisi variabel)
Untuk angka desimal presisi tetap, gunakan numeric (presisi 38 skala 9). Jika tidak, gunakan string bersama dengan variabel lapisan aplikasi library bilangan bulat presisi.
Jenis String text
varchar
string(max)

text dan varchar menyimpan serta memvalidasi string UTF-8. Di Spanner, kolom string harus menentukan panjang maksimumnya (tidak ada dampak pada penyimpanan; ini untuk tujuan validasi).
blob
bytes(max)
Untuk menyimpan data biner, gunakan jenis data bytes.
Jenis Tanggal dan Waktu date date
duration
int64
Spanner tidak mendukung jenis data durasi khusus. Gunakan int64 untuk menyimpan dengan durasi nanodetik.
time
int64
Spanner tidak mendukung jenis data waktu dalam hari khusus. Gunakan int64 untuk menyimpan offset nanosekon dalam sehari.
timestamp timestamp
Jenis Penampung Jenis yang ditentukan pengguna json atau proto
list array

Gunakan array untuk menyimpan daftar objek yang diketik.
map json atau proto

Spanner tidak mendukung jenis peta khusus. Gunakan json atau proto kolom yang mewakili peta. Untuk mengetahui informasi selengkapnya, lihat Menyimpan peta besar sebagai tabel sisipan.
set
array
Spanner tidak mendukung jenis kumpulan khusus. Gunakan array kolom untuk mewakili set, dengan aplikasi yang mengelola keunikan set. Untuk informasi selengkapnya, lihat Menyimpan peta besar sebagai tabel sisipan, yang juga dapat digunakan untuk menyimpan set besar.

Pola penggunaan dasar

Contoh kode berikut menunjukkan perbedaan antara kode klien Cassandra dan Spanner di Go. Untuk informasi selengkapnya, lihat library klien Spanner.

Inisialisasi klien

Di klien Cassandra, Anda membuat objek cluster yang mewakili cluster Cassandra, membuat instance objek sesi yang memisahkan koneksi ke cluster, dan mengeluarkan kueri pada sesi tersebut. Di Spanner, Anda membuat objek klien yang terikat ke database tertentu, dan mengeluarkan permintaan database pada objek klien.

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

Contoh Spanner

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

Membaca data

Operasi baca di Spanner dapat dilakukan melalui API gaya nilai kunci dan API kueri. Sebagai pengguna Cassandra, Anda mungkin merasa API kueri lebih familier. J perbedaan utama dalam Query API adalah bahwa Spanner memerlukan argumen bernama (tidak seperti argumen posisi ? di Cassandra). Nama dalam kueri Spanner harus diawali dengan @.

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

Contoh Spanner

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

Masukkan data

INSERT Cassandra setara dengan INSERT OR UPDATE Spanner. Anda harus menetapkan kunci utama lengkap untuk penyisipan. Spanner mendukung DML dan API mutasi gaya nilai kunci. Gaya nilai kunci mutation API direkomendasikan untuk operasi tulis sederhana karena latensi yang lebih rendah. Spanner DML API memiliki lebih banyak fitur karena mendukung platform SQL lengkap (termasuk penggunaan ekspresi dalam pernyataan DML).

Contoh Cassandra

Go

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

Contoh Spanner

Go

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

Menyisipkan data dalam batch

Di Cassandra, Anda dapat menyisipkan beberapa baris menggunakan pernyataan batch. Di Spanner, operasi commit dapat berisi beberapa mutasi. Spanner menyisipkan mutasi ini ke database secara atomik.

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

Contoh Spanner

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

Menghapus data

Penghapusan Cassandra memerlukan kunci utama baris yang akan dihapus. Hal ini mirip dengan mutasi DELETE di Spanner.

Contoh Cassandra

Go

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

Contoh Spanner

Go

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

Topik lanjutan

Bagian ini berisi informasi tentang cara menggunakan fitur Cassandra lanjutan di Spanner.

Tulis stempel waktu

Cassandra memungkinkan mutasi menentukan stempel waktu tulis secara eksplisit untuk sel tertentu menggunakan klausa USING TIMESTAMP. Biasanya, fitur ini digunakan untuk memanipulasi semantik kemenangan penulis terakhir dari Cassandra.

Spanner tidak mengizinkan klien menentukan stempel waktu setiap penulisan. Setiap sel ditandai secara internal dengan stempel waktu TrueTime pada waktu saat nilai sel di-commit. Karena Spanner memberikan konsisten dan dapat diserialisasi, sebagian besar aplikasi tidak memerlukan fungsi USING TIMESTAMP.

Jika Anda mengandalkan USING TIMESTAMP Cassandra untuk logika khusus aplikasi, Anda dapat menambahkan tambahan kolom TIMESTAMP ke skema Spanner, yang dapat melacak waktu modifikasi pada tingkat aplikasi. Pembaruan pada baris kemudian dapat digabungkan dalam transaksi baca-tulis. Contoh:

Contoh Cassandra

Go

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

Contoh Spanner

  1. Buat skema dengan kolom stempel waktu pembaruan eksplisit.

    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. Sesuaikan logika untuk memperbarui baris dan menyertakan stempel waktu.

    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
    }
  3. Periksa kondisi kustom sebelum memperbarui baris.

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

Mutasi bersyarat

Pernyataan INSERT ... IF EXISTS di Cassandra setara dengan INSERT di Spanner. Dalam kedua kasus tersebut, penyisipan akan gagal jika baris sudah ada.

Di Cassandra, Anda juga dapat membuat pernyataan DML yang menentukan kondisi, dan pernyataan akan gagal jika kondisi bernilai salah. Di Spanner, Anda dapat menggunakan mutasi UPDATE kondisional dalam transaksi baca-tulis. Misalnya, untuk memperbarui baris hanya jika terdapat kondisi tertentu:

Contoh Cassandra

Go

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

Contoh Spanner

  1. Sesuaikan logika untuk memperbarui baris dan menyertakan kondisi.

    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
    }
  2. Periksa kondisi kustom sebelum memperbarui baris.

    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 mendukung penetapan nilai time to live (TTL) di tingkat baris atau kolom. Di Spanner, TTL dikonfigurasi di tingkat baris, dan Anda menetapkan sebagai waktu kedaluwarsa untuk baris. Untuk mengetahui informasi selengkapnya, lihat Ringkasan waktu tunggu (TTL).

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

Contoh Spanner

  1. Membuat skema dengan kolom stempel waktu update yang eksplisit

    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. Menyisipkan baris dengan stempel waktu commit.

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

Menyimpan peta besar sebagai tabel sisipan.

Cassandra mendukung jenis map untuk menyimpan pasangan nilai kunci yang diurutkan. Untuk menyimpan jenis map yang berisi data dalam jumlah kecil di Spanner, Anda dapat menggunakan jenis JSON atau PROTO, yang memungkinkan Anda menyimpan data semi-terstruktur dan terstruktur. Pembaruan pada kolom tersebut mengharuskan seluruh nilai kolom ditulis ulang. Jika Anda memiliki kasus penggunaan dengan data dalam jumlah besar yang disimpan di map Cassandra, dan hanya sebagian kecil map yang perlu diperbarui, menggunakan tabel INTERLEAVED mungkin cocok. Misalnya, untuk menghubungkan sejumlah besar data nilai kunci dengan pengguna tertentu:

Contoh Cassandra

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

Contoh 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);

Dalam hal ini, baris lampiran pengguna disimpan dan disimpan bersama baris pengguna, dan dapat diambil serta diperbarui secara efisien bersama dengan baris pengguna. Anda dapat menggunakan fungsi read-write API di Spanner untuk berinteraksi dengan tabel sisipan. Untuk selengkapnya informasi tentang interleaving, Membuat tabel induk dan turunan.

Pengalaman developer

Bagian ini membandingkan Spanner dan Cassandra alat developer lainnya.

Pengembangan lokal

Anda dapat menjalankan Cassandra secara lokal untuk pengembangan dan pengujian unit. Spanner menyediakan lingkungan serupa untuk pengembangan lokal melalui emulator Spanner. Emulator menyediakan lingkungan fidelitas rendah untuk pengembangan interaktif dan pengujian unit. Untuk selengkapnya informasi selengkapnya, lihat Mengemulasi Spanner secara lokal.

Command line

Spanner yang setara dengan nodetool Cassandra adalah Google Cloud CLI. Anda dapat melakukan bidang kontrol dan data operasi pesawat menggunakan gcloud spanner. Untuk informasi selengkapnya, lihat Panduan referensi Google Cloud CLI Spanner.

Jika Anda memerlukan antarmuka REPL untuk mengeluarkan kueri ke Spanner mirip dengan cqlsh, Anda dapat menggunakan alat spanner-cli. Untuk menginstal dan menjalankan spanner-cli di Go:

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

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

Untuk mengetahui informasi selengkapnya, lihat repositori GitHub spanner-cli.