Halaman ini membahas skema dan memperkenalkan tabel yang disisipkan, yang dapat meningkatkan performa kueri saat membuat kueri tabel dalam hubungan induk-turunan.
Database Spanner berisi satu atau beberapa tabel. Tabel disusun sebagai baris dan kolom. Satu atau beberapa kolom didefinisikan sebagai kunci utama tabel, yang mengidentifikasi setiap baris secara unik. Kunci utama selalu diindeks untuk pencarian baris cepat, dan Anda dapat menentukan indeks sekunder di satu atau beberapa kolom. Jika Anda ingin memperbarui atau menghapus baris yang ada dalam tabel, tabel tersebut harus memiliki {i>primary key<i}. Tabel tanpa kolom {i>primary key <i}hanya dapat memiliki satu baris. Hanya database dialek GoogleSQL yang dapat memiliki tabel tanpa kunci utama.
Data di Spanner diketik dengan ketat. Anda harus menentukan skema untuk setiap database, dan skema tersebut harus menentukan jenis data setiap kolom dari setiap tabel. Jenis data mencakup jenis skalar dan kompleks, yang dijelaskan dalam Jenis data di GoogleSQL dan Jenis data PostgreSQL.
Hubungan tabel induk-turunan
Ada dua cara untuk menentukan hubungan induk-turunan di Spanner: table interleaving dan foreign key.
Penyertaan tabel Spanner adalah pilihan yang tepat untuk banyak hubungan
induk-turunan. Dengan interleaving, Spanner secara fisik menempatkan baris turunan bersama dengan baris induk di penyimpanan. Berbagi lokasi 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
Customers
yang disisipkan. 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
.
Anda mengaitkan tabel turunan dengan tabel induk menggunakan DDL yang mendeklarasikan tabel turunan sebagai disisipi di induk, dan dengan menyertakan kunci utama tabel induk sebagai bagian pertama dari kunci utama komposit tabel turunan. Untuk informasi interleaving selengkapnya, lihat Membuat tabel sisipan nanti dalam topik ini.
Kunci asing adalah solusi induk-turunan yang lebih umum dan menangani kasus penggunaan tambahan. Hubungan tersebut tidak terbatas pada kolom kunci utama, dan tabel dapat memiliki beberapa hubungan kunci asing, baik sebagai induk dalam beberapa hubungan maupun turunan dalam hubungan yang lain. Namun, hubungan kunci asing tidak berarti co-location tabel di lapisan penyimpanan.
Google merekomendasikan agar Anda memilih untuk merepresentasikan hubungan induk-turunan baik sebagai tabel sisipan atau kunci asing, tetapi tidak keduanya. Untuk mengetahui informasi selengkapnya tentang foreign key dan perbandingannya dengan tabel sisipan, lihat Ringkasan kunci asing.
Memilih {i>primary key<i}
Sering kali aplikasi Anda sudah memiliki kolom yang sesuai
untuk digunakan sebagai kunci utama. Misalnya, untuk tabel Customers
, mungkin ada CustomerId
yang disediakan aplikasi yang berfungsi sebagai kunci utama. Dalam kasus lain, Anda mungkin perlu membuat kunci utama saat
menyisipkan baris. Ini biasanya berupa nilai bilangan bulat unik tanpa signifikansi bisnis (kunci utama surrogate).
Dalam semua kasus, Anda harus berhati-hati untuk tidak membuat hotspot dengan pilihan kunci utama Anda. Misalnya, jika Anda menyisipkan kumpulan data dengan bilangan bulat yang meningkat secara monoton sebagai kunci, Anda akan selalu menyisipkannya di akhir spasi 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 menciptakan hotspot. Ada beberapa teknik yang dapat menyebarkan beban ke beberapa server dan menghindari hotspot:
- Melakukan hashing pada kunci dan menyimpannya di kolom. Gunakan kolom hash (atau kolom hash dan kolom kunci unik bersamaan) sebagai kunci utama.
- Tukar urutan kolom di kunci utama.
- Menggunakan Universally Unique Identifier (UUID). UUID versi 4 direkomendasikan karena menggunakan nilai acak dalam bit urutan tinggi. Jangan gunakan algoritma UUID (seperti UUID versi 1) yang menyimpan stempel waktu dalam bit urutan tinggi.
- Bit-reverse.
Menambahkan indeks sekunder berdasarkan kunci utama
Dalam situasi tertentu, penggunaan database Anda dapat memperoleh manfaat dari penambahan indeks sekunder berdasarkan kunci utama. Hal ini terutama berlaku jika Anda sering menjalankan kueri yang memerlukan pemindaian urutan terbalik dari kunci utama tabel.
Kunci utama dalam tabel sisipan
Untuk interleaving, setiap tabel harus memiliki {i>primary key<i}. Jika Anda mendeklarasikan tabel sebagai turunan sisipan dari tabel lain, tabel harus memiliki kunci utama gabungan 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 utama, dengan baris turunan disisipkan di antara baris induk. Lihat ilustrasi baris sisipan di Membuat tabel sisipan nanti dalam topik ini.
Singkatnya, Spanner dapat secara fisik menempatkan baris-baris tabel terkait. Contoh skema menunjukkan tampilan tata letak fisik ini.
Pemisahan database
Anda dapat menentukan hierarki hubungan induk-turunan yang disisipkan hingga tujuh lapisan, yang berarti Anda dapat bersama-sama menemukan baris tujuh tabel independen. Jika ukuran data di tabel Anda kecil, satu server Spanner mungkin dapat menangani database Anda. Namun, apa yang terjadi jika tabel terkait bertambah besar dan mulai mencapai batas resource di setiap server? Spanner adalah database terdistribusi, yang artinya seiring dengan pertumbuhan database Anda, Spanner membagi data Anda menjadi beberapa bagian yang disebut "pemisahan". Masing-masing bagian dapat berpindah secara independen dan ditetapkan ke server yang berbeda, yang dapat berada di lokasi fisik yang berbeda. Pemisahan memiliki rentang baris yang berdekatan. Kunci awal dan akhir rentang ini disebut "batas terpisah". Spanner secara otomatis menambahkan dan menghapus batas pemisahan berdasarkan ukuran dan beban, yang mengubah jumlah bagian dalam database.
Pemisahan 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 terpisah di antara setiap 10 baris tersebut sehingga masing-masing baris tersebut ditangani oleh server yang berbeda, bukan mengizinkan 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 meningkatkan setiap beberapa menit hingga Anda saturasi resource dalam instance atau terjadi kasus ketika tidak ada batas pemisahan baru yang dapat ditambahkan (karena Anda memiliki pemisahan yang hanya mencakup satu baris tanpa turunan yang disisipkan).
Contoh skema
Contoh skema di bawah ini menunjukkan cara membuat tabel induk dan turunan dengan dan tanpa interleaving, serta menggambarkan tata letak fisik data yang sesuai.
Membuat tabel induk
Misalkan Anda membuat aplikasi musik dan memerlukan tabel sederhana yang menyimpan baris data penyanyi:
Perhatikan bahwa tabel berisi satu kolom kunci utama, SingerId
, yang muncul
di sebelah kiri garis tebal, dan tabel disusun menurut baris
dan kolom.
Anda dapat menentukan tabel dengan skema Spanner seperti ini:
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ) PRIMARY KEY (SingerId);
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 didefinisikan sebagai turunan sisipan dari tabel lain).- Untuk database dialek GoogleSQL, kolom kunci utama biasanya dianotasi dengan
NOT NULL
(meskipun Anda dapat menghapus anotasi ini jika ingin mengizinkan nilaiNULL
di kolom kunci. Untuk informasi selengkapnya, lihat Kolom Kunci). - Kolom yang tidak disertakan dalam kunci utama disebut kolom non-kunci,
dan kolom tersebut dapat memiliki anotasi
NOT NULL
opsional. - Kolom yang menggunakan jenis
STRING
atauBYTES
di GoogleSQL harus ditentukan dengan panjang, yang mewakili jumlah maksimum karakter Unicode yang dapat disimpan dalam kolom. Spesifikasi panjang bersifat opsional untuk jenisvarchar
dancharacter 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 oleh kunci utama
("Singers(1)", lalu "Singers(2)", dan
seterusnya, dengan angka dalam tanda kurung adalah nilai kunci utama.
Diagram sebelumnya menggambarkan contoh batas pemisahan antara baris yang dikunci
oleh Singers(3)
dan Singers(4)
, dengan data dari hasil pemisahan
yang ditetapkan ke server yang berbeda. Seiring dengan bertambahnya jumlah tabel ini, baris data Singers
dapat disimpan di lokasi yang berbeda.
Membuat tabel induk dan turunan
Anggaplah sekarang Anda ingin menambahkan beberapa data dasar tentang setiap album penyanyi ke aplikasi musik.
Perhatikan bahwa kunci utama Albums
terdiri dari dua kolom: SingerId
dan
AlbumId
, untuk mengaitkan setiap album dengan penyanyinya. Contoh skema berikut
menentukan tabel Albums
dan Singers
di root hierarki
database, yang menjadikannya tabel seinduk.
-- Schema hierarchy: -- + Singers (sibling table of Albums) -- + Albums (sibling table of Singers)
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ) PRIMARY KEY (SingerId); 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 oleh kunci utama yang berdekatan, lalu
baris Singers
yang disimpan oleh kunci utama yang berdekatan:
Satu catatan penting tentang skema ini adalah Spanner mengasumsikan tidak ada
hubungan lokalitas data antara tabel Singers
dan Albums
, karena
tabel tersebut adalah tabel tingkat atas. Seiring pertumbuhan database, Spanner dapat menambahkan
batas-batas terpisah di antara baris-baris tersebut. Ini berarti baris tabel
Albums
dapat berada di bagian yang berbeda dari baris tabel Singers
, dan kedua bagian tersebut dapat berpindah secara independen satu sama lain.
Bergantung pada kebutuhan aplikasi Anda, mungkin Anda dapat mengizinkan data Albums
ditempatkan di bagian yang berbeda dari data Singers
. Namun, hal ini mungkin menimbulkan
masalah performa karena perlu untuk mengoordinasikan pembacaan dan update 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 disisipi
dari Singers
, yang ditempatkan bersama dengan baris dari dua tabel di sepanjang dimensi
kunci utama. Contoh berikutnya menjelaskan hal ini secara lebih detail.
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 yang terkait. Seperti yang disebutkan sebelumnya, kunci utama tabel induk harus menjadi bagian pertama dari kunci utama komposit tabel turunan.
Saat mendesain aplikasi musik, misalkan Anda menyadari bahwa aplikasi
harus sering mengakses baris dari tabel Albums
saat mengakses
baris Singers
. Misalnya, saat 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 dengan membuat Albums
sebagai
tabel turunan berselang-seling 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 dari Singers
.
GoogleSQL
CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ) PRIMARY KEY (SingerId); 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 turunanAlbums
, juga merupakan kunci utama dari tabel induknya,Singers
.- Anotasi
ON DELETE CASCADE
menandakan bahwa saat baris dari tabel induk dihapus, baris turunannya akan otomatis dihapus juga. Jika tabel turunan tidak memiliki anotasi ini, atau anotasinya adalahON DELETE NO ACTION
, Anda harus menghapus baris turunan sebelum dapat menghapus baris induk. - Baris yang disisipkan diurutkan pertama menurut baris tabel induk, lalu menurut baris yang berdampingan dari tabel turunan yang menggunakan kunci utama induk yang sama. Misalnya, "Penyanyi(1)", lalu "Albums(1, 1)", lalu "Albums(1, 2)", dan seterusnya.
- Hubungan lokalitas data dari setiap penyanyi dan data albumnya
dipertahankan jika database ini dipisah, asalkan ukuran baris
Singers
dan semua barisAlbums
tetap di bawah batas ukuran pemisahan dan tidak ada hotspot di barisAlbums
ini. - Baris induk harus ada sebelum Anda dapat menyisipkan baris turunan. Baris induk bisa sudah ada dalam database atau dapat disisipkan sebelum penyisipan baris turunan dalam transaksi yang sama.
Membuat hierarki tabel sisipan
Hubungan induk-turunan antara Singers
dan Albums
dapat diperluas ke
tabel turunan yang lebih banyak. Misalnya, Anda dapat membuat tabel sisipan yang disebut Songs
sebagai turunan dari Albums
untuk menyimpan daftar lagu setiap album:
Songs
harus memiliki kunci utama yang menyertakan semua kunci utama
tabel di atasnya 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, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX), ) PRIMARY KEY (SingerId); 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 mewakili tampilan fisik baris yang disisipi.
Dalam contoh ini, seiring bertambahnya jumlah penyanyi, Spanner menambahkan batas pemisahan antara 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 akan mencoba menambahkan batas pemisahan untuk memisahkan baris hotspot tersebut bersama dengan semua baris turunan di bawahnya.
Singkatnya, tabel induk beserta semua tabel turunannya dan turunan membentuk hierarki tabel dalam skema. Meskipun setiap tabel dalam hierarki independen secara logis, menyisipkannya secara fisik seperti ini dapat meningkatkan performa, dengan menggabungkan tabel-tabel tersebut terlebih dahulu dan memungkinkan Anda mengakses baris terkait bersama-sama sambil meminimalkan akses penyimpanan.
Menggabungkan dengan tabel sisipan
Jika memungkinkan, gabungkan data dalam tabel yang disisipi dengan {i>primary key<i}. Karena setiap baris yang disisipkan biasanya disimpan secara fisik dalam pemisahan yang sama dengan baris induknya, Spanner dapat melakukan penggabungan dengan kunci utama secara lokal, sehingga meminimalkan akses penyimpanan dan traffic jaringan. Dalam contoh berikut, Singers
dan Albums
digabungkan di 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;
Kolom kunci
Bagian ini menyertakan beberapa catatan tentang kolom utama.
Mengubah kunci tabel
Kunci tabel tidak dapat berubah; Anda tidak dapat menambahkan kolom kunci ke tabel yang ada atau menghapus kolom kunci dari tabel yang ada.
Menyimpan NULLs dalam kunci utama
Di GoogleSQL, jika Anda ingin menyimpan NULL di kolom kunci utama, hapus klausa NOT NULL
untuk kolom tersebut dalam skema. (Database dialek PostgreSQL tidak mendukung NULLs di kolom kunci utama.)
Berikut adalah contoh penghapusan klausa NOT NULL
pada kolom kunci utama SingerId
. Perhatikan bahwa karena SingerId
adalah kunci utama, hanya mungkin ada
satu baris yang menyimpan NULL
di kolom tersebut.
CREATE TABLE Singers ( SingerId INT64, FirstName STRING(1024), LastName STRING(1024), ) PRIMARY KEY (SingerId);
Properti nullable 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, FirstName STRING(1024), LastName STRING(1024), ) PRIMARY KEY (SingerId); 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 kunci tabel.
- Kolom kunci indeks.
Desain untuk multi-tenancy
Anda mungkin ingin mengimplementasikan multi-tenancy jika menyimpan data milik pelanggan yang berbeda. Misalnya, layanan musik mungkin ingin menyimpan konten 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
sendiri:
SingerId | FirstName | LastName |
---|---|---|
1 | Markas | Richards |
2 | Katalina | Smith |
SingerId | FirstName | LastName |
---|---|---|
1 | Alice | Trentor |
2 | Ganjar | Wright |
SingerId | FirstName | LastName |
---|---|---|
1 | Benjamin | Martinez |
2 | Hanna | Haris |
Multi-tenancy yang dikelola skema
Cara lain untuk mendesain multi-tenancy di Spanner adalah memiliki semua pelanggan dalam satu tabel dalam satu database, dan menggunakan nilai kunci utama yang berbeda untuk setiap pelanggan. Misalnya, Anda dapat menyertakan kolom kunci CustomerId
di tabel Anda. Jika Anda menjadikan CustomerId
sebagai kolom kunci pertama, 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 pemuatan. Pada contoh berikut,
ada satu tabel Singers
untuk semua pelanggan:
CustomerId | SingerId | FirstName | LastName |
---|---|---|---|
1 | 1 | Markas | Richards |
1 | 2 | Katalina | Smith |
2 | 1 | Alice | Trentor |
2 | 2 | Ganjar | Wright |
3 | 1 | Benjamin | Martinez |
3 | 2 | Hanna | Haris |
Jika Anda harus memiliki database terpisah untuk setiap tenant, ada batasan yang harus diperhatikan:
- Terdapat batas jumlah database per instance serta jumlah tabel dan indeks per database. Bergantung pada jumlah pelanggan, database atau tabel terpisah mungkin tidak dapat dimiliki.
- Penambahan tabel baru dan indeks non-disisipkan dapat memerlukan waktu lama. Anda mungkin tidak bisa mendapatkan performa yang diinginkan jika desain skema Anda bergantung pada penambahan tabel dan indeks baru.
Jika ingin membuat database terpisah, Anda mungkin akan lebih sukses jika mendistribusikan tabel ke berbagai 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, jangan menempatkan semua tabel dan indeks di database yang sama. Sebagai gantinya, bagi mereka di banyak database untuk mengurangi masalah performa ketika 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