Ringkasan skema

Halaman ini membahas persyaratan skema Spanner, cara menggunakan skema untuk membuat hubungan hierarkis, dan fitur skema. Fitur ini juga memperkenalkan tabel berselang-seling, yang dapat meningkatkan performa kueri saat membuat kueri tabel dalam hubungan induk-turunan.

Skema adalah namespace yang berisi objek database, seperti tabel, tampilan, indeks, dan fungsi. Anda menggunakan skema untuk mengatur objek, menerapkan hak istimewa kontrol akses terperinci, dan menghindari konflik penamaan. Anda harus menentukan skema untuk setiap database di Spanner.

Anda juga dapat menyegmentasikan dan menyimpan baris lebih lanjut dalam tabel database di berbagai wilayah geografis. Untuk mengetahui informasi selengkapnya, lihat Ringkasan partisi geografis.

Data yang diketik secara kuat

Data di Spanner diketik dengan ketat. Jenis data mencakup jenis skalar dan kompleks, yang dijelaskan dalam Jenis data di GoogleSQL dan Jenis data PostgreSQL.

Pilih kunci utama

Database Spanner dapat berisi satu atau beberapa tabel. Tabel disusun sebagai baris dan kolom. Skema tabel menentukan satu atau beberapa kolom tabel sebagai kunci utama tabel yang secara unik mengidentifikasi setiap baris. Kunci utama selalu diindeks untuk pencarian baris yang cepat. Jika Anda ingin memperbarui atau menghapus baris yang ada dalam tabel, tabel tersebut harus memiliki kunci utama. Tabel tanpa kolom kunci utama hanya dapat memiliki satu baris. Hanya database dialek GoogleSQL yang dapat memiliki tabel tanpa kunci utama.

Sering kali aplikasi Anda sudah memiliki kolom yang cocok untuk digunakan sebagai kunci utama. Misalnya, untuk tabel Customers, mungkin ada CustomerId yang disediakan aplikasi dan berfungsi dengan baik sebagai kunci utama. Dalam kasus lain, Anda mungkin perlu membuat kunci utama saat menyisipkan baris. Kolom ini biasanya berupa nilai bilangan bulat unik tanpa signifikansi bisnis (kunci utama pengganti).

Dalam semua kasus, Anda harus berhati-hati agar tidak membuat hotspot dengan pilihan kunci utama Anda. Misalnya, jika Anda menyisipkan rekaman dengan bilangan bulat yang meningkat secara monoton sebagai kunci, Anda akan selalu menyisipkan di akhir ruang kunci. Hal ini tidak diinginkan karena Spanner membagi data di antara server berdasarkan rentang kunci, yang berarti penyisipan Anda akan diarahkan ke satu server, sehingga membuat hotspot. Ada teknik yang dapat menyebarkan beban di beberapa server dan menghindari hotspot:

Hubungan tabel induk-turunan

Ada dua cara untuk menentukan hubungan induk-turunan di Spanner: penyisipan tabel dan kunci asing.

Sisipan tabel Spanner adalah pilihan yang tepat untuk banyak hubungan induk-turunan. Dengan interleaving, Spanner secara fisik menempatkan baris turunan bersama baris induk dalam penyimpanan. Lokasi yang sama dapat meningkatkan performa secara signifikan. Misalnya, jika Anda memiliki tabel Customers dan tabel Invoices, dan aplikasi Anda sering mengambil semua invoice untuk pelanggan, Anda dapat menentukan Invoices sebagai tabel turunan yang disisipkan dari Customers. Dengan demikian, Anda mendeklarasikan hubungan lokalitas data antara dua tabel independen. Anda memberi tahu Spanner untuk menyimpan satu atau beberapa baris Invoices dengan satu baris Customers. Hubungan induk-turunan ini diterapkan saat disisipkan dengan klausa INTERLEAVE IN PARENT. Tabel turunan INTERLEAVE IN memiliki karakteristik penyisipan baris fisik yang sama, tetapi Spanner tidak menerapkan integritas referensial antara induk dan turunan.

Anda mengaitkan tabel turunan dengan tabel induk menggunakan DDL yang mendeklarasikan tabel turunan sebagai tabel yang disisipkan di tabel induk, dan dengan menyertakan kunci utama tabel induk sebagai bagian pertama dari kunci utama gabungan tabel turunan.

Untuk mengetahui informasi selengkapnya tentang penyisipan, lihat Membuat tabel yang disisipkan.

Kunci asing adalah solusi induk-turunan yang lebih umum dan menangani kasus penggunaan tambahan. Tabel tidak terbatas pada kolom kunci utama, dan tabel dapat memiliki beberapa hubungan kunci asing, baik sebagai induk dalam beberapa hubungan maupun turunan dalam hubungan lainnya. Namun, hubungan kunci asing tidak mengimplikasikan lokasi yang sama untuk tabel di lapisan penyimpanan.

Google merekomendasikan agar Anda memilih untuk mewakili hubungan induk-turunan sebagai tabel sisipan atau sebagai kunci asing, tetapi tidak keduanya. Untuk mengetahui informasi selengkapnya tentang kunci asing dan perbandingannya dengan tabel yang di-interleave, lihat Ringkasan kunci asing.

Kunci utama dalam tabel sisipan

Untuk interleaving, setiap tabel harus memiliki kunci utama. Jika Anda mendeklarasikan tabel sebagai turunan yang disisipkan dari tabel lain, tabel tersebut harus memiliki kunci utama komposit yang mencakup semua komponen kunci utama induk, dalam urutan yang sama, dan biasanya, satu atau beberapa kolom tabel turunan tambahan.

Spanner menyimpan baris dalam urutan yang diurutkan berdasarkan nilai kunci primer, dengan baris turunan disisipkan di antara baris induk. Lihat ilustrasi baris bersisipan di Membuat tabel bersisipan di bagian selanjutnya di halaman ini.

Singkatnya, Spanner dapat secara fisik menempatkan baris tabel terkait di tempat yang sama. Contoh skema menunjukkan tampilan tata letak fisik ini.

Pemisahan database

Anda dapat menentukan hierarki hubungan induk-turunan yang saling terkait hingga tujuh lapisan, yang berarti Anda dapat menempatkan baris dari tujuh tabel independen secara bersamaan. Jika ukuran data dalam tabel Anda kecil, satu server Spanner mungkin dapat menangani database Anda. Namun, apa yang terjadi saat tabel terkait Anda bertambah besar dan mulai mencapai batas resource server individual? Spanner adalah database terdistribusi, yang berarti bahwa seiring berkembangnya database Anda, Spanner membagi data Anda menjadi beberapa bagian yang disebut "bagian". Setiap pemisahan dapat bergerak secara independen satu sama lain dan ditetapkan ke server yang berbeda, yang dapat berada di lokasi fisik yang berbeda. Pemisahan berisi rentang baris yang berdekatan. Kunci awal dan akhir rentang ini disebut "batas pemisahan". Spanner otomatis menambahkan dan menghapus batas pemisahan berdasarkan ukuran dan beban, yang mengubah jumlah pemisahan dalam database.

Pembagian berbasis beban

Sebagai contoh cara Spanner melakukan pemisahan berbasis beban untuk mengurangi hotspot baca, misalkan database Anda berisi tabel dengan 10 baris yang lebih sering dibaca daripada semua baris lainnya dalam tabel. Spanner dapat menambahkan batas pemisahan di antara setiap 10 baris tersebut sehingga masing-masing ditangani oleh server yang berbeda, dan bukan membiarkan semua pembacaan baris tersebut menggunakan resource satu server.

Sebagai aturan umum, jika Anda mengikuti praktik terbaik untuk desain skema, Spanner dapat mengurangi hotspot sehingga throughput baca akan meningkat setiap beberapa menit hingga Anda mencapai batas sumber daya di instance atau mengalami kasus di mana tidak ada batas pemisahan baru yang dapat ditambahkan (karena Anda memiliki pemisahan yang hanya mencakup satu baris tanpa turunan yang disisipkan).

Skema bernama

Skema bernama membantu Anda mengatur data serupa secara bersamaan. Hal ini membantu Anda menemukan objek dengan cepat di konsol Google Cloud , menerapkan hak istimewa, dan menghindari konflik penamaan.

Skema bernama, seperti objek database lainnya, dikelola menggunakan DDL.

Skema bernama Spanner memungkinkan Anda menggunakan nama yang sepenuhnya memenuhi syarat (FQNs) untuk membuat kueri data. Dengan FQN, Anda dapat menggabungkan nama skema dan nama objek untuk mengidentifikasi objek database. Misalnya, Anda dapat membuat skema yang disebut warehouse untuk unit bisnis gudang. Tabel yang menggunakan skema ini dapat mencakup: product, order, dan customer information. Atau Anda dapat membuat skema bernama fulfillment untuk unit bisnis pemenuhan. Skema ini juga dapat memiliki tabel yang disebut product, order, dan customer information. Dalam contoh pertama, FQN adalah warehouse.product dan dalam contoh kedua, FQN adalah fulfillment.product. Hal ini mencegah kebingungan dalam situasi ketika beberapa objek memiliki nama yang sama.

Dalam DDL CREATE SCHEMA, objek tabel diberi FQN, misalnya, sales.customers, dan nama pendek, misalnya, sales.

Objek database berikut mendukung skema bernama:

  • TABLE
    • CREATE
    • INTERLEAVE IN [PARENT]
    • FOREIGN KEY
    • SYNONYM
  • VIEW
  • INDEX
  • FOREIGN KEY
  • SEQUENCE

Untuk mengetahui informasi selengkapnya tentang cara menggunakan skema bernama, lihat Mengelola skema bernama.

Menggunakan kontrol akses yang sangat terperinci dengan skema bernama

Skema bernama memungkinkan Anda memberikan akses tingkat skema ke setiap objek dalam skema. Hal ini berlaku untuk objek skema yang ada pada saat Anda memberikan akses. Anda harus memberikan akses ke objek yang ditambahkan nanti.

Kontrol akses terperinci membatasi akses ke seluruh grup objek database, seperti tabel, kolom, dan baris dalam tabel.

Untuk mengetahui informasi selengkapnya, lihat Memberikan hak istimewa kontrol akses terperinci ke skema bernama.

Contoh skema

Contoh skema di bagian ini menunjukkan cara membuat tabel induk dan turunan dengan dan tanpa penyisipan, serta mengilustrasikan tata letak fisik data yang sesuai.

Membuat tabel induk

Misalnya, Anda membuat aplikasi musik dan memerlukan tabel yang menyimpan baris data penyanyi:

Tabel penyanyi dengan lima baris dan empat kolom

Perhatikan bahwa tabel berisi satu kolom kunci utama, SingerId, yang muncul di sebelah kiri garis tebal, dan tabel disusun berdasarkan baris dan kolom.

Anda dapat menentukan tabel dengan DDL berikut:

GoogleSQL

CREATE TABLE Singers (
SingerId   INT64 NOT NULL PRIMARY KEY,
FirstName  STRING(1024),
LastName   STRING(1024),
SingerInfo BYTES(MAX),
);

PostgreSQL

CREATE TABLE singers (
singer_id   BIGINT PRIMARY KEY,
first_name  VARCHAR(1024),
last_name   VARCHAR(1024),
singer_info BYTEA
);

Perhatikan hal-hal berikut tentang skema contoh:

  • Singers adalah tabel di root hierarki database (karena tidak ditentukan sebagai turunan yang disisipkan dari tabel lain).
  • Untuk database dialek GoogleSQL, kolom kunci utama biasanya diberi anotasi dengan NOT NULL (meskipun Anda dapat menghapus anotasi ini jika ingin mengizinkan nilai NULL di kolom kunci. Untuk mengetahui informasi selengkapnya, lihat Kolom Kunci).
  • Kolom yang tidak disertakan dalam kunci utama disebut kolom non-kunci, dan dapat memiliki anotasi NOT NULL opsional.
  • Kolom yang menggunakan jenis STRING atau BYTES di GoogleSQL harus ditentukan dengan panjang, yang menunjukkan jumlah maksimum karakter Unicode yang dapat disimpan di kolom. Spesifikasi panjang bersifat opsional untuk jenis varchar dan character varying PostgreSQL. Untuk mengetahui informasi selengkapnya, lihat Jenis Data Skalar untuk database dialek GoogleSQL dan Jenis data PostgreSQL untuk database dialek PostgreSQL.

Seperti apa tata letak fisik baris dalam tabel Singers? Diagram berikut menunjukkan baris tabel Singers yang disimpan berdasarkan kunci utama ("Penyanyi(1)", lalu "Penyanyi(2)", dengan angka dalam tanda kurung adalah nilai kunci utama.

Contoh baris tabel yang disimpan dalam urutan kunci utama

Diagram sebelumnya menggambarkan contoh batas pemisahan antara baris yang dikelompokkan menurut Singers(3) dan Singers(4), dengan data dari pemisahan yang dihasilkan ditetapkan ke server yang berbeda. Seiring bertambahnya ukuran tabel ini, baris data Singers dapat disimpan di lokasi yang berbeda.

Membuat tabel induk dan turunan

Asumsikan bahwa sekarang Anda ingin menambahkan beberapa data dasar tentang album setiap penyanyi ke aplikasi musik.

Tabel album dengan lima baris dan tiga kolom

Perhatikan bahwa kunci utama Albums terdiri dari dua kolom: SingerId dan AlbumId, untuk mengaitkan setiap album dengan penyanyinya. Skema contoh berikut menentukan tabel Albums dan Singers di root hierarki database, yang menjadikannya tabel saudara.

-- Schema hierarchy:
-- + Singers (sibling table of Albums)
-- + Albums (sibling table of Singers)

GoogleSQL

CREATE TABLE Singers (
 SingerId   INT64 NOT NULL PRIMARY KEY,
 FirstName  STRING(1024),
 LastName   STRING(1024),
 SingerInfo BYTES(MAX),
);

CREATE TABLE Albums (
SingerId     INT64 NOT NULL,
AlbumId      INT64 NOT NULL,
AlbumTitle   STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId);

PostgreSQL

CREATE TABLE singers (
singer_id   BIGINT PRIMARY KEY,
first_name  VARCHAR(1024),
last_name   VARCHAR(1024),
singer_info BYTEA
);

CREATE TABLE albums (
singer_id     BIGINT,
album_id      BIGINT,
album_title   VARCHAR,
PRIMARY KEY (singer_id, album_id)
);

Tata letak fisik baris Singers dan Albums terlihat seperti diagram berikut, dengan baris tabel Albums yang disimpan berdasarkan kunci utama yang berdekatan, lalu baris Singers yang disimpan berdasarkan kunci utama yang berdekatan:

Tata letak fisik baris

Satu catatan penting tentang skema ini adalah Spanner mengasumsikan tidak ada hubungan lokalitas data antara tabel Singers dan Albums, karena keduanya adalah tabel tingkat teratas. Seiring berkembangnya database, Spanner dapat menambahkan batas pemisahan di antara baris mana pun. Artinya, baris tabel Albums dapat berakhir dalam pemisahan yang berbeda dari baris tabel Singers, dan kedua pemisahan dapat bergerak secara independen satu sama lain.

Bergantung pada kebutuhan aplikasi Anda, tidak masalah jika data Albums berada di pemisahan yang berbeda dari data Singers. Namun, hal ini dapat menimbulkan penurunan performa karena perlu mengoordinasikan pembacaan dan pembaruan di seluruh resource yang berbeda. Jika aplikasi Anda sering kali perlu mengambil informasi tentang semua album untuk penyanyi tertentu, Anda harus membuat Albums sebagai tabel turunan yang disisipkan dari Singers, yang mengelompokkan baris dari kedua tabel di sepanjang dimensi kunci utama. Contoh berikutnya menjelaskan hal ini secara lebih mendetail.

Membuat tabel sisipan

Tabel sisipan adalah tabel yang Anda deklarasikan sebagai turunan sisipan dari tabel lain karena Anda ingin baris tabel turunan disimpan secara fisik dengan baris induk terkait. Seperti yang disebutkan sebelumnya, kunci utama tabel induk harus menjadi bagian pertama dari kunci utama gabungan tabel turunan.

Setelah Anda menyisipkan tabel, tindakan ini bersifat permanen. Anda tidak dapat mengurungkan penyisipan. Sebagai gantinya, Anda harus membuat tabel lagi dan memigrasikan data ke tabel tersebut.

Saat mendesain aplikasi musik, Anda menyadari bahwa aplikasi perlu sering mengakses baris dari tabel Albums saat mengakses baris Singers. Misalnya, saat Anda mengakses baris Singers(1), Anda juga perlu mengakses baris Albums(1, 1) dan Albums(1, 2). Dalam hal ini, Singers dan Albums harus memiliki hubungan lokalitas data yang kuat. Anda dapat mendeklarasikan hubungan lokalitas data ini dengan membuat Albums sebagai tabel turunan yang disisipkan dari Singers.

-- Schema hierarchy:
-- + Singers
--   + Albums (interleaved table, child table of Singers)

Baris yang dicetak tebal dalam skema berikut menunjukkan cara membuat Albums sebagai tabel berselang-seling Singers.

GoogleSQL

CREATE TABLE Singers (
 SingerId   INT64 NOT NULL PRIMARY KEY,
 FirstName  STRING(1024),
 LastName   STRING(1024),
 SingerInfo BYTES(MAX),
 );

CREATE TABLE Albums (
 SingerId     INT64 NOT NULL,
 AlbumId      INT64 NOT NULL,
 AlbumTitle   STRING(MAX),
 ) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

PostgreSQL

CREATE TABLE singers (
 singer_id   BIGINT PRIMARY KEY,
 first_name  VARCHAR(1024),
 last_name   VARCHAR(1024),
 singer_info BYTEA
 );

CREATE TABLE albums (
 singer_id     BIGINT,
 album_id      BIGINT,
 album_title   VARCHAR,
 PRIMARY KEY (singer_id, album_id)
 )
 INTERLEAVE IN PARENT singers ON DELETE CASCADE;

Catatan tentang skema ini:

  • SingerId, yang merupakan bagian pertama dari kunci utama tabel turunan Albums, juga merupakan kunci utama tabel induknya Singers.
  • Anotasi ON DELETE CASCADE menandakan bahwa saat baris dari tabel induk dihapus, baris turunannya juga akan otomatis dihapus. Jika tabel turunan tidak memiliki anotasi ini, atau anotasi adalah ON DELETE NO ACTION, maka Anda harus menghapus baris turunan sebelum dapat menghapus baris induk.
  • Baris yang disisipkan diurutkan terlebih dahulu berdasarkan baris tabel induk, lalu berdasarkan baris berdekatan tabel turunan yang berbagi kunci utama induk. Misalnya, "Penyanyi(1)", lalu "Album(1, 1)", dan kemudian "Album(1, 2)".
  • Hubungan lokalitas data setiap penyanyi dan data albumnya dipertahankan jika database ini dibagi, asalkan ukuran baris Singers dan semua baris Albums-nya tetap di bawah batas ukuran pemisahan dan tidak ada hotspot di salah satu baris Albums ini.
  • Baris induk harus ada sebelum Anda dapat menyisipkan baris turunan. Baris induk dapat sudah ada di database atau dapat disisipkan sebelum penyisipan baris turunan dalam transaksi yang sama.

Baris Album diselingi dengan baris Penyanyi

Misalnya, Anda ingin memodelkan Projects dan Resources sebagai tabel yang diselingi. Skenario tertentu dapat memanfaatkan INTERLEAVE IN - kemampuan untuk tidak memerlukan keberadaan baris Projects agar entitas di bawahnya ada (misalnya, Project telah dihapus, tetapi resource-nya perlu dibersihkan sebelum dihapus).

GoogleSQL

CREATE TABLE Projects (
  ProjectId   INT64 NOT NULL,
  ProjectName STRING(1024),
) PRIMARY KEY (ProjectId);

CREATE TABLE Resources (
  ProjectId    INT64 NOT NULL,
  ResourceId   INT64 NOT NULL,
  ResourceName STRING(1024),
) PRIMARY KEY (ProjectId, ResourceId),
  INTERLEAVE IN Projects;

PostgreSQL

CREATE TABLE Projects (
  ProjectId   BIGINT PRIMARY KEY,
  ProjectName VARCHAR(1024),
);

CREATE TABLE Resources (
  ProjectId    BIGINT,
  ResourceId   BIGINT,
  ResourceName VARCHAR(1024),
  PRIMARY KEY (ProjectId, ResourceId)
) INTERLEAVE IN Projects;

Perhatikan bahwa dalam contoh ini kita menggunakan klausa INTERLEAVE IN Projects, bukan INTERLEAVE IN PARENT Projects. Hal ini menunjukkan bahwa kami tidak menerapkan hubungan induk-turunan antara Project dan Resource.

Dalam contoh ini, baris Resources(1, 10) dan Resources(1, 20) dapat ada di database meskipun baris Projects(1) tidak ada. Projects(1) dapat dihapus meskipun Resources(1, 10) dan Resources(1, 20) masih ada, dan penghapusan tidak memengaruhi baris Resources ini.

Membuat hierarki tabel yang diselingi

Hubungan induk-turunan antara Singers dan Albums dapat diperluas ke tabel turunan lainnya. Misalnya, Anda dapat membuat tabel berselang-seling yang disebut Songs sebagai turunan dari Albums untuk menyimpan daftar lagu setiap album:

Tabel lagu dengan enam baris dan empat kolom

Songs harus memiliki kunci utama yang mencakup semua kunci utama tabel yang berada di tingkat yang lebih tinggi dalam hierarki, yaitu SingerId dan AlbumId.

-- Schema hierarchy:
-- + Singers
--   + Albums (interleaved table, child table of Singers)
--     + Songs (interleaved table, child table of Albums)

GoogleSQL

CREATE TABLE Singers (
 SingerId   INT64 NOT NULL PRIMARY KEY,
 FirstName  STRING(1024),
 LastName   STRING(1024),
 SingerInfo BYTES(MAX),
);

CREATE TABLE Albums (
 SingerId     INT64 NOT NULL,
 AlbumId      INT64 NOT NULL,
 AlbumTitle   STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId),
 INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE TABLE Songs (
 SingerId     INT64 NOT NULL,
 AlbumId      INT64 NOT NULL,
 TrackId      INT64 NOT NULL,
 SongName     STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId, TrackId),
 INTERLEAVE IN PARENT Albums ON DELETE CASCADE;

PostgreSQL

CREATE TABLE singers (
 singer_id   BIGINT PRIMARY KEY,
 first_name  VARCHAR(1024),
 last_name   VARCHAR(1024),
 singer_info BYTEA
 );

CREATE TABLE albums (
 singer_id     BIGINT,
 album_id      BIGINT,
 album_title   VARCHAR,
 PRIMARY KEY (singer_id, album_id)
 )
 INTERLEAVE IN PARENT singers ON DELETE CASCADE;

CREATE TABLE songs (
 singer_id     BIGINT,
 album_id      BIGINT,
 track_id      BIGINT,
 song_name     VARCHAR,
 PRIMARY KEY (singer_id, album_id, track_id)
 )
 INTERLEAVE IN PARENT albums ON DELETE CASCADE;

Diagram berikut menunjukkan tampilan fisik baris yang disisipkan.

Lagu diselingi dalam Album, yang diselingi di antara Penyanyi

Dalam contoh ini, seiring bertambahnya jumlah penyanyi, Spanner menambahkan batas pemisahan antar-penyanyi untuk mempertahankan lokalitas data antara penyanyi dan data album serta lagunya. Namun, jika ukuran baris penyanyi dan baris turunannya melebihi batas ukuran pemisahan, atau hotspot terdeteksi di baris turunan, Spanner mencoba menambahkan batas pemisahan untuk mengisolasi baris hotspot tersebut beserta semua baris turunan di bawahnya.

Singkatnya, tabel induk beserta semua tabel turunan dan tabel keturunannya membentuk hierarki tabel dalam skema. Meskipun setiap tabel dalam hierarki secara logis independen, menyisipkan tabel secara fisik dengan cara ini dapat meningkatkan performa, secara efektif menggabungkan tabel sebelumnya dan memungkinkan Anda mengakses baris terkait secara bersamaan sambil meminimalkan akses penyimpanan.

Gabungan dengan tabel sisipan

Jika memungkinkan, gabungkan data dalam tabel yang diselingi berdasarkan kunci utama. Karena setiap baris yang disisipkan biasanya disimpan secara fisik dalam pemisahan yang sama dengan baris induknya, Spanner dapat melakukan penggabungan berdasarkan kunci utama secara lokal, sehingga meminimalkan akses penyimpanan dan traffic jaringan. Dalam contoh berikut, Singers dan Albums digabungkan pada kunci utama SingerId.

GoogleSQL

SELECT s.FirstName, a.AlbumTitle
FROM Singers AS s JOIN Albums AS a ON s.SingerId = a.SingerId;

PostgreSQL

SELECT s.first_name, a.album_title
FROM singers AS s JOIN albums AS a ON s.singer_id = a.singer_id;

Grup lokalitas

Spanner menggunakan grup lokalitas untuk mempertahankan hubungan lokalitas data di seluruh kolom tabel. Jika Anda tidak membuat grup lokalitas secara eksplisit untuk tabel, Spanner akan mengelompokkan semua kolom ke dalam grup lokalitas default dan menyimpan data semua tabel di penyimpanan SSD. Anda dapat menggunakan grup lokalitas untuk melakukan hal berikut:

  • Gunakan penyimpanan bertingkat. Penyimpanan bertingkat adalah fitur penyimpanan yang dikelola sepenuhnya yang memungkinkan Anda memilih apakah akan menyimpan data di solid-state drive (SSD) atau hard disk drive (HDD). Secara default, tanpa menggunakan penyimpanan bertingkat, Spanner menyimpan semua data di penyimpanan SSD.

  • Gunakan pengelompokan kolom untuk menyimpan kolom tertentu secara terpisah dari kolom lainnya. Karena data untuk kolom yang ditentukan disimpan secara terpisah, membaca data dari kolom tersebut lebih cepat daripada jika semua data dikelompokkan bersama. Untuk menggunakan pengelompokan kolom, Anda perlu membuat grup lokalitas tanpa menentukan opsi penyimpanan bertingkat. Spanner menggunakan grup lokalitas untuk menyimpan kolom yang ditentukan secara terpisah. Jika ditentukan, kolom akan mewarisi kebijakan penyimpanan bertingkat dari tabel atau grup lokalitas default. Kemudian, gunakan pernyataan DDL CREATE TABLE untuk menetapkan grup lokalitas untuk kolom yang ditentukan atau gunakan pernyataan DDL ALTER TABLE untuk mengubah grup lokalitas yang digunakan oleh kolom tabel. Pernyataan DDL menentukan kolom yang disimpan dalam grup lokalitas. Terakhir, Anda dapat membaca data di kolom ini dengan lebih efisien.

Kolom utama

Bagian ini mencakup beberapa catatan tentang kolom utama.

Mengubah kunci tabel

Kunci tabel tidak dapat diubah; Anda tidak dapat menambahkan kolom kunci ke tabel yang ada atau menghapus kolom kunci dari tabel yang ada.

Menyimpan NULL di kunci utama

Di GoogleSQL, jika Anda ingin menyimpan NULL di kolom kunci utama, hilangkan klausa NOT NULL untuk kolom tersebut dalam skema. (Database dialek PostgreSQL tidak mendukung nilai NULL di kolom kunci primer.)

Berikut adalah contoh penghapusan klausa NOT NULL pada kolom kunci utama SingerId. Perhatikan bahwa karena SingerId adalah kunci utama, hanya boleh ada satu baris yang menyimpan NULL di kolom tersebut.

CREATE TABLE Singers (
  SingerId   INT64 PRIMARY KEY,
  FirstName  STRING(1024),
  LastName   STRING(1024),
);

Properti yang dapat bernilai null dari kolom kunci utama harus cocok antara deklarasi tabel induk dan turunan. Dalam contoh ini, NOT NULL untuk kolom Albums.SingerId tidak diizinkan karena Singers.SingerId menghilangkannya.

CREATE TABLE Singers (
  SingerId   INT64 PRIMARY KEY,
  FirstName  STRING(1024),
  LastName   STRING(1024),
);

CREATE TABLE Albums (
  SingerId     INT64 NOT NULL,
  AlbumId      INT64 NOT NULL,
  AlbumTitle   STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

Jenis yang tidak diizinkan

Kolom berikut tidak boleh berjenis ARRAY:

  • Kolom utama tabel.
  • Kolom kunci indeks.

Desain untuk multi-tenancy

Anda mungkin ingin menerapkan multi-tenancy jika menyimpan data yang dimiliki oleh pelanggan yang berbeda. Misalnya, layanan musik mungkin ingin menyimpan konten setiap label rekaman secara terpisah.

Multi-tenancy klasik

Cara klasik untuk mendesain multi-tenancy adalah dengan membuat database terpisah untuk setiap pelanggan. Dalam contoh ini, setiap database memiliki tabel Singers-nya sendiri:

Database 1: Ackworth Records
SingerId FirstName LastName
1MarcRichards
2CatalinaSmith
Database 2: Cama Records
SingerId FirstName LastName
1AliceTrentor
2GabrielWright
Database 3: Eagan Records
SingerId FirstName LastName
1BenjaminMartinez
2HannahHarris

Multi-tenancy yang dikelola skema

Cara lain untuk mendesain multi-tenancy di Spanner adalah dengan menempatkan semua pelanggan dalam satu tabel di satu database, dan menggunakan nilai kunci utama yang berbeda untuk setiap pelanggan. Misalnya, Anda dapat menyertakan kolom kunci CustomerId dalam tabel. Jika Anda menjadikan CustomerId sebagai kolom kunci pertama, maka data untuk setiap pelanggan akan memiliki lokalitas yang baik. Spanner kemudian dapat menggunakan pemisahan database secara efektif untuk memaksimalkan performa berdasarkan ukuran data dan pola beban. Dalam contoh berikut, ada satu tabel Singers untuk semua pelanggan:

Database multi-tenancy Spanner
CustomerId SingerId FirstName LastName
11MarcRichards
12CatalinaSmith
21AliceTrentor
22GabrielWright
31BenjaminMartinez
32HannahHarris

Jika Anda harus memiliki database terpisah untuk setiap tenant, ada batasan yang harus diperhatikan:

  • Ada batasan jumlah database per instance dan jumlah tabel dan indeks per database. Bergantung pada jumlah pelanggan, Anda mungkin tidak dapat memiliki database atau tabel terpisah.
  • Menambahkan tabel baru dan indeks non-interleaved dapat memerlukan waktu yang lama. Anda mungkin tidak dapat memperoleh performa yang diinginkan jika desain skema Anda bergantung pada penambahan tabel dan indeks baru.

Jika ingin membuat database terpisah, Anda mungkin akan lebih berhasil jika mendistribusikan tabel di seluruh database sedemikian rupa sehingga setiap database memiliki jumlah perubahan skema yang rendah per minggu.

Jika Anda membuat tabel dan indeks terpisah untuk setiap pelanggan aplikasi Anda, jangan masukkan semua tabel dan indeks ke dalam database yang sama. Sebagai gantinya, pisahkan indeks di banyak database untuk mengurangi masalah performa saat membuat indeks dalam jumlah besar.

Untuk mempelajari lebih lanjut pola pengelolaan data dan desain aplikasi lainnya untuk multi-tenancy, lihat Menerapkan Multi-Tenancy di Spanner