Dokumen ini menjelaskan cara membuat kueri yang efisien dengan menggunakan praktik terbaik untuk mendesain skema Spanner Graph. Anda dapat melakukan iterasi pada desain skema, jadi sebaiknya identifikasi terlebih dahulu pola kueri penting untuk memandu desain skema Anda.
Untuk mengetahui informasi umum tentang praktik terbaik desain skema Spanner, lihat Praktik terbaik desain skema.
Mengoptimalkan penelusuran tepi
Penelusuran tepi adalah proses menavigasi grafik dengan mengikuti tepinya, dimulai dari node tertentu dan bergerak di sepanjang tepi yang terhubung untuk mencapai node lain. Arah tepi ditentukan oleh skema. Penelusuran tepi adalah operasi mendasar dalam Spanner Graph, sehingga peningkatan efisiensi penelusuran tepi sangat penting untuk performa aplikasi Anda.
Anda dapat melintasi tepi dalam dua arah:
forward edge traversal: mengikuti tepi keluar dari node sumber.
traversal tepi terbalik: mengikuti tepi masuk dari node tujuan.
Mengingat seseorang, contoh kueri berikut melakukan traversal tepi maju dari
tepi Owns
:
GRAPH FinGraph
MATCH (person:Person {id: 1})-[owns:Owns]->(accnt:Account)
RETURN accnt.id;
Dengan akun tertentu, kueri contoh berikut melakukan penelusuran tepi terbalik dari tepi Owns
:
GRAPH FinGraph
MATCH (accnt:Account {id: 1})<-[owns:Owns]-(person:Person)
RETURN person.name;
Mengoptimalkan traversal edge penerusan menggunakan penyisipan
Untuk meningkatkan performa traversal tepi penerusan, selang-selingi tabel input tepi ke dalam tabel input node sumber untuk mengelokasikan tepi dengan node sumber. Penyisipan adalah teknik pengoptimalan penyimpanan di Spanner yang secara fisik menempatkan baris tabel turunan bersama baris induk yang sesuai di penyimpanan. Untuk mengetahui informasi selengkapnya tentang penyisipan, lihat Ringkasan skema.
Contoh berikut menunjukkan praktik terbaik ini:
CREATE TABLE Person (
id INT64 NOT NULL,
name STRING(MAX),
) PRIMARY KEY (id);
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
Mengoptimalkan penelusuran tepi terbalik menggunakan kunci asing
Untuk melintasi tepi terbalik secara efisien, buat batasan kunci asing yang diterapkan antara tepi dan node tujuan. Kunci asing yang diterapkan ini membuat indeks sekunder di tepi yang dikunci oleh kunci node tujuan. Indeks sekunder otomatis digunakan selama eksekusi kueri.
Contoh berikut menunjukkan praktik terbaik ini:
CREATE TABLE Person (
id INT64 NOT NULL,
name STRING(MAX),
) PRIMARY KEY (id);
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id);
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id),
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
Mengoptimalkan traversal tepi terbalik menggunakan indeks sekunder
Jika tidak ingin membuat kunci asing yang diterapkan pada edge, misalnya, karena integritas data yang ketat yang diterapkan, Anda dapat langsung membuat indeks sekunder pada tabel input edge, seperti yang ditunjukkan dalam contoh berikut:
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
CREATE INDEX AccountOwnedByPerson
ON PersonOwnAccount (account_id), INTERLEAVE IN Account;
INTERLEAVE IN
mendeklarasikan hubungan lokalitas data antara indeks sekunder dan tabel yang disisipkan (Account
, dalam contoh). Dengan penyisipan, baris indeks sekunder AccountOwnedByPerson
ditempatkan bersama dengan baris yang sesuai dari tabel Account
. Untuk mengetahui informasi selengkapnya tentang interleaving, lihat Hubungan tabel induk-turunan. Untuk mengetahui informasi selengkapnya tentang indeks penyisipan, lihat Indeks dan penyisipan.
Mengoptimalkan penelusuran tepi menggunakan kunci asing informasional
Jika skenario Anda memiliki hambatan performa penulisan yang disebabkan oleh kunci asing yang diterapkan, seperti saat Anda sering memperbarui node hub yang memiliki banyak edge yang terhubung, pertimbangkan untuk menggunakan kunci asing informasional. Menggunakan kunci asing informasional pada kolom yang mereferensikan tabel edge akan membantu pengoptimal kueri menghilangkan pemindaian tabel node yang berlebihan. Namun, karena kunci asing informatif tidak memerlukan indeks sekunder pada tabel edge, kunci asing tersebut tidak meningkatkan kecepatan pencarian saat kueri mencoba menemukan edge menggunakan node akhir. Untuk mengetahui informasi selengkapnya, lihat Perbandingan jenis kunci asing.
Penting untuk dipahami bahwa jika aplikasi Anda tidak dapat menjamin integritas referensial, penggunaan kunci asing informasional untuk pengoptimalan kueri dapat menyebabkan hasil kueri yang salah.
Contoh berikut membuat tabel dengan kunci asing informasional pada kolom
account_id
:
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Account FOREIGN KEY (account_id)
REFERENCES Account (id) NOT ENFORCED
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
Jika penyisipan bukan opsi, Anda dapat menandai kedua referensi tepi dengan kunci asing informasi, seperti dalam contoh berikut:
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Person FOREIGN KEY (id)
REFERENCES Person (id) NOT ENFORCED,
CONSTRAINT FK_Account FOREIGN KEY (account_id)
REFERENCES Account (id) NOT ENFORCED
) PRIMARY KEY (id, account_id);
Jangan biarkan tepi menggantung
Tepi yang tidak terhubung adalah tepi yang menghubungkan kurang dari dua node. Tepi yang tidak terhubung dapat terjadi saat node dihapus tanpa menghapus tepi terkaitnya, atau saat tepi dibuat tanpa menautkannya dengan benar ke node-nya.
Tidak mengizinkan ujung yang tidak terhubung memberikan manfaat berikut:
- Menerapkan integritas struktur grafik.
- Meningkatkan performa kueri dengan menghindari pekerjaan tambahan untuk memfilter tepi yang tidak memiliki endpoint.
Melarang ujung yang menggantung menggunakan batasan referensial
Untuk melarang ujung yang tidak terhubung, tentukan batasan pada kedua endpoint:
- Selang-selingi tabel input tepi ke dalam tabel input node sumber. Pendekatan ini memastikan bahwa node sumber dari tepi selalu ada.
- Buat batasan kunci asing yang diterapkan pada edge untuk memastikan bahwa node tujuan edge selalu ada.
Contoh berikut menggunakan penyisipan dan kunci asing yang diterapkan untuk menerapkan integritas referensial:
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id) ON DELETE CASCADE,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
Gunakan ON DELETE CASCADE untuk menghapus tepi secara otomatis saat menghapus node
Saat Anda menggunakan penyisipan atau kunci asing yang diterapkan untuk melarang ujung yang menggantung,
gunakan klausa ON DELETE
untuk mengontrol perilaku saat Anda ingin menghapus
node dengan ujung yang masih terlampir. Untuk mengetahui informasi selengkapnya, lihat
Menghapus secara bertingkat untuk tabel yang disisipkan
dan
Tindakan kunci asing.
Anda dapat menggunakan ON DELETE
dengan cara berikut:
ON DELETE NO ACTION
(atau menghapus klausaON DELETE
): Menghapus node dengan tepi akan gagal.ON DELETE CASCADE
: Menghapus node akan otomatis menghapus tepi terkait dalam transaksi yang sama.
Cascade penghapusan untuk tepi yang menghubungkan berbagai jenis node
Menghapus tepi saat node sumber dihapus. Misalnya,
INTERLEAVE IN PARENT Person ON DELETE CASCADE
menghapus semua edgePersonOwnAccount
keluar dari nodePerson
yang dihapus. Untuk mengetahui informasi selengkapnya, lihat Membuat tabel yang di-interleave.Menghapus tepi saat node tujuan dihapus. Misalnya,
CONSTRAINT FK_Account FOREIGN KEY(account_id) REFERENCES Account(id) ON DELETE CASCADE
menghapus semua edgePersonOwnAccount
masuk ke dalam nodeAccount
yang dihapus.
Cascade penghapusan untuk tepi yang menghubungkan jenis node yang sama
Jika node sumber dan tujuan dari suatu edge memiliki jenis yang sama dan
edge disisipkan ke dalam node sumber, Anda dapat menentukan ON DELETE CASCADE
hanya untuk node sumber atau node tujuan (tetapi tidak untuk kedua node).
Untuk menghapus tepi yang tidak terhubung dalam kedua kasus, buat kunci asing yang diterapkan pada referensi node sumber tepi, bukan menyisipkan tabel input tepi ke dalam tabel input node sumber.
Sebaiknya lakukan interleaving untuk
mengoptimalkan traversal tepi penerusan.
Pastikan untuk memverifikasi dampaknya pada beban kerja Anda sebelum melanjutkan. Lihat contoh
berikut, yang menggunakan AccountTransferAccount
sebagai tabel
input tepi:
--Define two Foreign Keys, each on one end Node of Transfer Edge, both with ON DELETE CASCADE action:
CREATE TABLE AccountTransferAccount (
id INT64 NOT NULL,
to_id INT64 NOT NULL,
amount FLOAT64,
create_time TIMESTAMP NOT NULL,
order_number STRING(MAX),
CONSTRAINT FK_FromAccount FOREIGN KEY (id) REFERENCES Account (id) ON DELETE CASCADE,
CONSTRAINT FK_ToAccount FOREIGN KEY (to_id) REFERENCES Account (id) ON DELETE CASCADE,
) PRIMARY KEY (id, to_id);
Memfilter menurut properti node atau edge dengan indeks sekunder
Indeks sekunder sangat penting untuk pemrosesan kueri yang efisien. Indeks ini mendukung pencarian cepat node dan tepi berdasarkan nilai properti tertentu, tanpa harus menjelajahi seluruh struktur grafik. Hal ini penting saat Anda bekerja dengan grafik besar, karena melintasi semua node dan tepi dapat sangat tidak efisien.
Mempercepat pemfilteran node menurut properti
Untuk mempercepat pemfilteran menurut properti node, buat indeks sekunder pada
properti. Misalnya, kueri berikut menemukan akun untuk nama panggilan
tertentu. Tanpa indeks sekunder, semua node Account
dipindai untuk mencocokkan kriteria pemfilteran.
GRAPH FinGraph
MATCH (acct:Account)
WHERE acct.nick_name = "abcd"
RETURN acct.id;
Untuk mempercepat kueri, buat indeks sekunder pada properti yang difilter, seperti yang ditunjukkan dalam contoh berikut:
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
is_blocked BOOL,
nick_name STRING(MAX),
) PRIMARY KEY (id);
CREATE INDEX AccountByNickName
ON Account (nick_name);
Tips: Gunakan indeks yang difilter dengan NULL untuk properti sparse. Untuk mengetahui informasi selengkapnya, lihat Menonaktifkan pengindeksan nilai NULL.
Mempercepat penelusuran edge penerusan dengan pemfilteran pada properti edge
Saat melintasi tepi sambil memfilter propertinya, Anda dapat mempercepat kueri dengan membuat indeks sekunder pada properti tepi dan menyisipkan indeks ke dalam node sumber.
Misalnya, kueri berikut menemukan akun yang dimiliki oleh orang tertentu setelah waktu tertentu:
GRAPH FinGraph
MATCH (person:Person)-[owns:Owns]->(acct:Account)
WHERE person.id = 1
AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
RETURN acct.id;
Secara default, kueri ini membaca semua edge orang yang ditentukan, lalu memfilter
edge yang memenuhi kondisi pada create_time
.
Contoh berikut menunjukkan cara meningkatkan efisiensi kueri dengan membuat
indeks sekunder pada referensi node sumber edge (id
) dan properti edge
(create_time
). Gabungkan indeks di bawah tabel input node sumber untuk
menempatkan indeks bersama node sumber.
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
CREATE INDEX PersonOwnAccountByCreateTime
ON PersonOwnAccount (id, create_time)
INTERLEAVE IN Person;
Dengan menggunakan pendekatan ini, kueri dapat secara efisien menemukan semua tepi yang memenuhi
kondisi pada create_time
.
Mempercepat penelusuran edge terbalik dengan pemfilteran pada properti edge
Saat melintasi edge terbalik sambil memfilter propertinya, Anda dapat mempercepat kueri dengan membuat indeks sekunder menggunakan node tujuan dan properti edge untuk pemfilteran.
Contoh kueri berikut melakukan traversal sisi terbalik dengan pemfilteran pada properti sisi:
GRAPH FinGraph
MATCH (acct:Account)<-[owns:Owns]-(person:Person)
WHERE acct.id = 1
AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
RETURN person.id;
Untuk mempercepat kueri ini menggunakan indeks sekunder, gunakan salah satu opsi berikut:
Buat indeks sekunder pada referensi node tujuan edge (
account_id
) dan properti edge (create_time
), seperti yang ditunjukkan pada contoh berikut:CREATE TABLE PersonOwnAccount ( id INT64 NOT NULL, account_id INT64 NOT NULL, create_time TIMESTAMP, ) PRIMARY KEY (id, account_id), INTERLEAVE IN PARENT Person ON DELETE CASCADE; CREATE INDEX PersonOwnAccountByCreateTime ON PersonOwnAccount (account_id, create_time);
Pendekatan ini memberikan performa yang lebih baik karena sisi terbalik diurutkan berdasarkan
account_id
dancreate_time
, yang memungkinkan mesin kueri menemukan sisi untukaccount_id
secara efisien yang memenuhi kondisi padacreate_time
. Namun, jika pola kueri yang berbeda memfilter properti yang berbeda, maka setiap properti mungkin memerlukan indeks terpisah, yang dapat menambah overhead.Buat indeks sekunder pada referensi node tujuan edge (
account_id
) dan simpan properti edge (create_time
) dalam kolom penyimpanan, seperti yang ditunjukkan dalam contoh berikut:CREATE TABLE PersonOwnAccount ( id INT64 NOT NULL, account_id INT64 NOT NULL, create_time TIMESTAMP, ) PRIMARY KEY (id, account_id), INTERLEAVE IN PARENT Person ON DELETE CASCADE; CREATE INDEX PersonOwnAccountByCreateTime ON PersonOwnAccount (account_id) STORING (create_time);
Pendekatan ini dapat menyimpan beberapa properti; namun, kueri harus membaca semua tepi node tujuan, lalu memfilter properti tepi.
Anda dapat menggabungkan pendekatan ini dengan mengikuti pedoman berikut:
- Gunakan properti edge di kolom indeks jika digunakan dalam kueri yang penting untuk performa.
- Untuk properti yang digunakan dalam kueri yang kurang sensitif terhadap performa, tambahkan properti tersebut di kolom penyimpanan.
Jenis node dan edge model dengan label dan properti
Jenis node dan tepi biasanya dimodelkan dengan label. Namun, Anda juga dapat menggunakan
properti untuk memodelkan jenis. Pertimbangkan contoh yang memiliki banyak jenis akun yang berbeda, seperti BankAccount
, InvestmentAccount
, dan RetirementAccount
. Anda dapat menyimpan akun dalam tabel input terpisah dan
memodelkannya sebagai label terpisah, atau Anda dapat menyimpan akun dalam satu tabel input
dan menggunakan properti untuk membedakan jenisnya.
Mulai proses pembuatan model dengan membuat model jenis dengan label. Pertimbangkan untuk menggunakan properti dalam skenario berikut.
Meningkatkan pengelolaan skema
Jika grafik Anda memiliki banyak jenis node dan tepi yang berbeda, mengelola tabel input terpisah untuk setiap jenis dapat menjadi sulit. Untuk mempermudah pengelolaan skema, buat model jenis sebagai properti.
Jenis model dalam properti untuk mengelola jenis yang sering berubah
Saat Anda memodelkan jenis sebagai label, penambahan atau penghapusan jenis memerlukan perubahan pada skema. Jika Anda melakukan terlalu banyak pembaruan skema dalam jangka waktu yang singkat, Spanner mungkin membatasi pemrosesan pembaruan skema yang diantrekan. Untuk mengetahui informasi selengkapnya, lihat Membatasi frekuensi update skema.
Jika Anda perlu sering mengubah skema, sebaiknya buat model jenis dalam properti untuk mengatasi batasan frekuensi pembaruan skema.
Mempercepat kueri
Jenis pemodelan dengan properti dapat mempercepat kueri saat pola node atau tepi mereferensikan beberapa label. Contoh kueri berikut menemukan semua instance
SavingsAccount
dan InvestmentAccount
yang dimiliki oleh Person
, dengan asumsi jenis
akun dimodelkan dengan label:
GRAPH FinGraph
MATCH (:Person {id: 1})-[:Owns]->(acct:SavingsAccount|InvestmentAccount)
RETURN acct.id;
Pola node acct
mereferensikan dua label. Jika ini adalah kueri penting untuk performa, pertimbangkan untuk memodelkan Account
menggunakan properti. Pendekatan
ini dapat memberikan performa kueri yang lebih baik, seperti yang ditunjukkan dalam contoh kueri
berikut. Sebaiknya Anda melakukan tolok ukur kedua kueri.
GRAPH FinGraph
MATCH (:Person {id: 1})-[:Owns]->(acct:Account)
WHERE acct.type IN ("Savings", "Investment")
RETURN acct.id;
Simpan jenis toko di kunci elemen node untuk mempercepat kueri
Untuk mempercepat kueri dengan pemfilteran pada jenis node saat jenis node dimodelkan dengan properti dan jenisnya tidak berubah selama masa aktif node, ikuti langkah-langkah berikut:
- Sertakan properti sebagai bagian dari kunci elemen node.
- Tambahkan jenis node di tabel input edge.
- Sertakan jenis node dalam kunci perujukan edge.
Contoh berikut menerapkan pengoptimalan ini ke node Account
dan
edge AccountTransferAccount
.
CREATE TABLE Account (
type STRING(MAX) NOT NULL,
id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (type, id);
CREATE TABLE AccountTransferAccount (
type STRING(MAX) NOT NULL,
id INT64 NOT NULL,
to_type STRING(MAX) NOT NULL,
to_id INT64 NOT NULL,
amount FLOAT64,
create_time TIMESTAMP NOT NULL,
order_number STRING(MAX),
) PRIMARY KEY (type, id, to_type, to_id),
INTERLEAVE IN PARENT Account ON DELETE CASCADE;
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
Account
)
EDGE TABLES (
AccountTransferAccount
SOURCE KEY (type, id) REFERENCES Account
DESTINATION KEY (to_type, to_id) REFERENCES Account
);
Mengonfigurasi TTL pada node dan tepi
Time to live (TTL) Spanner adalah mekanisme yang mendukung habis masa berlaku dan penghapusan data secara otomatis setelah jangka waktu tertentu. Hal ini sering digunakan untuk data yang memiliki masa aktif atau relevansi terbatas, seperti informasi sesi, cache sementara, atau log peristiwa. Dalam kasus ini, TTL membantu mempertahankan ukuran dan performa database.
Contoh berikut menggunakan TTL untuk menghapus akun 90 hari setelah penutupannya:
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
close_time TIMESTAMP,
) PRIMARY KEY (id),
ROW DELETION POLICY (OLDER_THAN(close_time, INTERVAL 90 DAY));
Jika tabel node memiliki TTL dan tabel edge yang disisipkan di dalamnya, penyisipan
harus ditentukan dengan ON DELETE CASCADE
. Demikian pula, jika
tabel node memiliki TTL dan direferensikan oleh tabel edge melalui kunci
asing, kunci asing harus ditentukan dengan ON DELETE CASCADE
untuk mempertahankan
integritas referensial, atau ditentukan sebagai kunci asing informasi untuk memungkinkan
keberadaan edge yang tidak terhubung.
Dalam contoh berikut, AccountTransferAccount
disimpan hingga sepuluh tahun
selama akun tetap aktif. Jika akun dihapus, histori transfer juga akan dihapus.
CREATE TABLE AccountTransferAccount (
id INT64 NOT NULL,
to_id INT64 NOT NULL,
amount FLOAT64,
create_time TIMESTAMP NOT NULL,
order_number STRING(MAX),
) PRIMARY KEY (id, to_id),
INTERLEAVE IN PARENT Account ON DELETE CASCADE,
ROW DELETION POLICY (OLDER_THAN(create_time, INTERVAL 3650 DAY));
Menggabungkan tabel input node dan edge
Anda dapat menggunakan tabel input yang sama untuk menentukan lebih dari satu node dan tepi dalam skema Anda.
Dalam contoh tabel berikut, node Account
memiliki kunci gabungan
(owner_id, account_id)
. Ada definisi edge implisit, node Person
dengan kunci (id
) memiliki node Account
dengan kunci gabungan
(owner_id, account_id)
jika id
sama dengan owner_id
.
CREATE TABLE Person (
id INT64 NOT NULL,
) PRIMARY KEY (id);
-- Assume each account has exactly one owner.
CREATE TABLE Account (
owner_id INT64 NOT NULL,
account_id INT64 NOT NULL,
) PRIMARY KEY (owner_id, account_id);
Dalam hal ini, Anda dapat menggunakan tabel input Account
untuk menentukan node Account
dan tepi PersonOwnAccount
, seperti yang ditunjukkan dalam contoh skema berikut.
Untuk memastikan bahwa semua nama tabel elemen unik, contoh ini memberikan alias Owns
pada definisi tabel edge.
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
Person,
Account
)
EDGE TABLES (
Account AS Owns
SOURCE KEY (owner_id) REFERENCES Person
DESTINATION KEY (owner_id, account_id) REFERENCES Account
);
Langkah berikutnya
- Membuat, memperbarui, atau menghapus skema Spanner Graph.
- Menyisipkan, memperbarui, atau menghapus data Spanner Graph.