Dokumen ini menjelaskan praktik terbaik untuk menyesuaikan performa kueri Spanner Graph, yang mencakup pengoptimalan berikut:
- Menghindari pemindaian penuh tabel input untuk node dan edge.
- Mengurangi jumlah data yang perlu dibaca kueri dari penyimpanan.
- Mengurangi ukuran data perantara.
Mulai dari node kardinalitas yang lebih rendah
Tulis traversal jalur sehingga dimulai dengan node kardinalitas yang lebih rendah. Pendekatan ini membuat kumpulan hasil perantara tetap kecil, dan mempercepat eksekusi kueri.
Misalnya, kueri berikut memiliki semantik yang sama:
Traversal edge maju:
GRAPH FinGraph MATCH (p:Person {name:"Alex"})-[:Owns]->(a:Account {is_blocked: true}) RETURN p.id AS person_id, a.id AS account_id;Traversal edge mundur:
GRAPH FinGraph MATCH (a:Account {is_blocked:true})<-[:Owns]-(p:Person {name: "Alex"}) RETURN p.id AS person_id, a.id AS account_id;
Dengan asumsi bahwa ada lebih sedikit orang dengan nama Alex daripada akun yang diblokir, sebaiknya tulis kueri ini dalam traversal edge maju.
Memulai dari node kardinalitas yang lebih rendah sangat penting untuk traversal jalur dengan panjang variabel. Contoh berikut menunjukkan cara yang direkomendasikan untuk menemukan akun yang berada dalam tiga transfer dari akun tertentu.
GRAPH FinGraph
MATCH (:Account {id: 7})-[:Transfers]->{1,3}(a:Account)
RETURN a.id;
Menentukan semua label secara default
Spanner Graph menyimpulkan node dan label edge yang memenuhi syarat jika label dihilangkan. Sebaiknya tentukan label untuk semua node dan edge jika memungkinkan, karena inferensi ini mungkin tidak selalu memungkinkan dan dapat menyebabkan lebih banyak label daripada yang diperlukan untuk dipindai.
Pernyataan MATCH tunggal
Contoh berikut menemukan akun yang ditautkan oleh maksimal 3 transfer dari akun yang diberikan:
GRAPH FinGraph
MATCH (src:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id;
Di seluruh pernyataan MATCH
Tentukan label pada node dan edge saat keduanya merujuk ke elemen yang sama, tetapi berada di seluruh pernyataan MATCH.
Contoh berikut menunjukkan pendekatan yang direkomendasikan ini:
GRAPH FinGraph
MATCH (acct:Account {id: 7})-[:Transfers]->{1,3}(other_acct:Account)
RETURN acct, COUNT(DISTINCT other_acct) AS related_accts
GROUP BY acct
NEXT
MATCH (acct:Account)<-[:Owns]-(p:Person)
RETURN p.id AS person, acct.id AS acct, related_accts;
Menggunakan IS_FIRST untuk mengoptimalkan kueri
Anda dapat menggunakan fungsi
IS_FIRST
untuk meningkatkan performa kueri dengan mengambil sampel edge dan membatasi traversal
dalam grafik. Fungsi ini membantu menangani node kardinalitas tinggi dan mengoptimalkan kueri multi-hop.
Jika ukuran sampel yang Anda tentukan terlalu kecil, kueri mungkin tidak menampilkan data. Oleh karena itu, Anda mungkin perlu mencoba ukuran sampel yang berbeda untuk menemukan keseimbangan optimal antara data yang ditampilkan dan performa kueri yang ditingkatkan.
Contoh IS_FIRST ini menggunakan FinGraph, grafik keuangan dengan node Account dan edge Transfers untuk transfer uang. Untuk membuat FinGraph dan menggunakannya
untuk menjalankan kueri contoh, lihat
Menyiapkan dan membuat kueri Spanner Graph.
Membatasi edge yang dilalui untuk meningkatkan performa kueri
Saat Anda membuat kueri grafik, beberapa node dapat memiliki jumlah edge masuk atau keluar yang jauh lebih besar dibandingkan dengan node lainnya. Node kardinalitas tinggi ini terkadang disebut node super atau node hub. Node super dapat menyebabkan masalah performa karena traversal melalui node tersebut mungkin melibatkan pemrosesan data dalam jumlah besar, yang menyebabkan kemiringan data dan waktu eksekusi yang lama.
Untuk mengoptimalkan kueri grafik dengan node super, gunakan fungsi IS_FIRST dalam klausa FILTER untuk membatasi jumlah edge yang dilalui kueri dari node. Karena akun di FinGraph mungkin memiliki jumlah transaksi yang jauh lebih tinggi daripada yang lain, Anda dapat menggunakan IS_FIRST untuk mencegah kueri yang tidak efisien. Teknik ini sangat berguna jika Anda tidak memerlukan enumerasi lengkap dari semua koneksi dari node super.
Kueri berikut menemukan akun (a2) yang menerima transfer secara langsung atau tidak langsung dari akun yang diblokir (a1). Kueri menggunakan IS_FIRST untuk mencegah performa lambat saat akun memiliki banyak transfer dengan membatasi jumlah edge Transfers yang akan dipertimbangkan untuk setiap Account.
GRAPH FinGraph
MATCH
(a1:Account {is_blocked: true})
-[e:Transfers WHERE e IN
{
MATCH -[selected_e:Transfers]->
FILTER IS_FIRST(@max_transfers_per_account) OVER (
PARTITION BY SOURCE_NODE_ID(selected_e)
ORDER BY selected_e.create_time DESC)
RETURN selected_e
}
]->{1,5}
(a2:Account)
RETURN a1.id AS src_id, a2.id AS dst_id;
Contoh ini menggunakan hal berikut:
@max_transfers_per_account: Parameter kueri yang menentukan jumlah maksimum edgeTransfersyang akan dipertimbangkan untuk setiap akun (a1).PARTITION BY SOURCE_NODE_ID(selected_e): Memastikan bahwa batasIS_FIRSTberlaku secara independen untuk setiap akun (a1).ORDER BY selected_e.create_time DESC: Menentukan bahwa transfer terbaru akan ditampilkan.
Mengambil sampel node perantara untuk mengoptimalkan kueri multi-hop
Anda juga dapat meningkatkan efisiensi kueri dengan menggunakan IS_FIRST untuk mengambil sampel node perantara dalam kueri multi-hop. Teknik ini meningkatkan efisiensi dengan membatasi jumlah jalur yang dipertimbangkan kueri untuk setiap node perantara. Untuk melakukannya, pisahkan kueri multi-hop menjadi beberapa pernyataan MATCH yang dipisahkan oleh NEXT, dan terapkan IS_FIRST di titik tengah tempat Anda perlu mengambil sampel:
GRAPH FinGraph
MATCH (a1:Account {is_blocked: true})-[e1:Transfers]->(a2:Account)
FILTER IS_FIRST(1) OVER (PARTITION BY a2)
RETURN a1, a2
NEXT
MATCH (a2)-[e2:Transfers]->(a3:Account)
RETURN a1.id AS src_id, a2.id AS mid_id, a3.id AS dst_id;
Untuk memahami cara IS_FIRST mengoptimalkan kueri ini:
Klausa
FILTER IS_FIRST(1) OVER (PARTITION BY a2)diterapkan dalam pernyataanMATCHpertama.Untuk setiap node akun perantara (
a2),IS_FIRSThanya mempertimbangkan edgeTransfersmasuk pertama (e1), sehingga mengurangi jumlah jalur yang akan dieksplorasi dalam pernyataanMATCHkedua.Efisiensi kueri dua hop secara keseluruhan ditingkatkan karena
MATCHkedua tidak memproses data yang tidak diperlukan, terutama saata2memiliki banyak transfer masuk.
Menggunakan eksekusi terfaktorisasi untuk mengoptimalkan kueri
Optimalkan performa kueri Spanner Graph dengan memfaktorisasi kueri yang melintasi pola grafik dan membuat hasil perantara duplikat.
Kueri grafik dapat dieksekusi dalam mode terfaktorisasi, yang menghapus duplikat awalan jalur kardinalitas tinggi sebelum melintasi jalur lainnya. Pengoptimalan ini meningkatkan performa kueri dengan mengurangi perbandingan kunci berulang atau probe tabel hash selama eksekusi kueri grafik. Untuk memfaktorisasi kueri, tambahkan
@{factorized_mode} petunjuk
ke traversal pola individual atau di tingkat kueri.
Tetapkan factorized_mode ke salah satu nilai berikut:
factorize_left: Mengoptimalkan traversal grafik saat banyak jalur bertemu di beberapa node. Spanner Graph menghapus duplikat jalur berdasarkan ID node tujuan, lalu mengambil properti node untuk mengurangi pekerjaan yang berlebihan.factorize_both: Mengoptimalkan kueri non-linear dengan beberapa sub-jalur yang berbagi node. Spanner Graph menghindari pembuatan hasil perantara untuk setiap jalur secara independen. Sebagai gantinya, Spanner Graph menghitung setiap sub-jalur satu kali, lalu menggabungkannya, sehingga menunda produk silang hasil hingga akhir. Gunakan ini untuk pola seperti segitiga atau cabang.
Untuk mengetahui informasi selengkapnya tentang petunjuk traversal, lihat Petunjuk grafik.
Contoh berikut menunjukkan cara menggunakan petunjuk mode terfaktorisasi untuk meningkatkan performa kueri grafik. Untuk menjalankan kode contoh, buat skema FinGraph di
Menyiapkan dan membuat kueri Spanner Graph.
Mengurangi hasil perantara dalam kueri linear
Akun penipuan sering menerima transaksi dalam volume tinggi. Kueri yang dirancang untuk mengidentifikasi akun ini dapat menghasilkan hasil perantara yang besar, yang sebagian besar merupakan duplikat karena bertemu di sekumpulan kecil akun tujuan. Mengambil properti node penipuan dari hasil perantara yang berlebihan ini memerlukan komputasi yang tidak perlu, yang dapat memperlambat performa kueri secara signifikan.
Petunjuk @{factorized_mode} mengatasi masalah ini dengan mengoptimalkan eksekusi kueri.
Contoh berikut menunjukkan cara menggunakan petunjuk ini untuk mengambil detail akun dan transaksi. Contoh ini melacak transfer yang berasal dari akun yang diblokir dan diketahui ke akun yang berpotensi penipuan yang berjarak satu hingga tiga hop. Versi kueri yang tidak dioptimalkan ini tersedia di tab kedua.
Kueri terfaktorisasi
GRAPH FinGraph
MATCH (a1:Account {is_blocked: true})-[e1:Transfers]->{1,3}@{factorized_mode=factorize_left}(a2:Account)
LET total_amount = SUM(e1.amount)
RETURN a2.id, a2.create_time, total_amount;
Kueri yang tidak dioptimalkan
GRAPH FinGraph
MATCH (a1:Account {is_blocked: true})-[e1:Transfers]->{1,3}(a2:Account)
LET total_amount = SUM(e1.amount)
RETURN a2.id, a2.create_time, total_amount;
Jika jumlah node tujuan yang berbeda a2 jauh lebih kecil daripada jumlah jalur yang mengarah ke a2, teknik ini akan mencegah pekerjaan yang berlebihan. Hal ini umum terjadi di jaringan penipuan, tempat perantara mentransfer uang ke beberapa penerima. Petunjuk terfaktorisasi melakukan tindakan berikut:
Menemukan semua jalur yang menyertakan satu hingga tiga edge yang dimulai dari node yang diblokir, dan jumlah transfer di sepanjang jalur tersebut.
Menentukan jumlah node tujuan yang berbeda
a2.Mengambil
create_timeuntuk setiap node tujuan yang berbedaa2hanya satu kali.Menggabungkan
create_timeuntuk setiap nodea2dengan daftar jalur yang mengarah kea2untuk membentuk hasil kueri.
Menunda pembuatan hasil perantara dalam kueri non-linear yang kompleks
Kueri grafik sering kali memiliki struktur yang kompleks, yang terdiri dari beberapa pola sub-jalur linear. Contoh berikut menunjukkan kueri yang menganalisis transaksi yang berasal dari akun dengan id sama dengan 1. Kueri ini mengategorikan semua akun tujuan berdasarkan create_time ke dalam tiga bucket yang berbeda:
- Dalam 30 hari terakhir
- 30 hingga 90 hari yang lalu
- Lebih lama dari 90 hari
Kueri menampilkan semua tiga kali dan jumlah transaksi total untuk transaksi ini.
Petunjuk @{factorized_mode=factorize_both} mengoptimalkan eksekusi kueri dengan menghindari materialisasi hasil perantara awal. Petunjuk ini menggunakan fakta bahwa semua
sub-jalur dalam klausa MATCH secara kondisional independen, mengingat nilai
node awal a. factorize_both mengevaluasi dua sub-jalur pertama secara independen dan tidak mengulangi evaluasi sub-jalur terakhir.
Kueri terfaktorisasi
GRAPH FinGraph
MATCH (a:Account {id: 1})-[e1:Transfers]->(a1:Account),
@{factorized_mode=factorize_both}(a:Account)-[e2:Transfers]->(a2:Account),
(a:Account)-[e3:Transfers]->(a3:Account)
WHERE e1.create_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 90 DAY)
AND e2.create_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 90 DAY)
AND e2.create_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
AND e3.create_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
LET total_amount = e1.amount + e2.amount + e3.amount
RETURN a1.id as a1_id, a2.id as a2_id, a3.id as a3_id, total_amount;
Kueri yang tidak dioptimalkan
GRAPH FinGraph
MATCH (a:Account {id: 1})-[e1:Transfers]->(a1:Account),
(a:Account)-[e2:Transfers]->(a2:Account),
(a:Account)-[e3:Transfers]->(a3:Account)
WHERE e1.create_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 90 DAY)
AND e2.create_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 90 DAY)
AND e2.create_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
AND e3.create_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
LET total_amount = e1.amount + e2.amount + e3.amount
RETURN a1.id as a1_id, a2.id as a2_id, a3.id as a3_id, total_amount;
Untuk akun awal tertentu dengan id sama dengan 1, setiap edge Transfers keluar mengarah ke beberapa node tujuan independen. Perhatikan contoh berikut:
Madalah jumlah akun (a1) yang memiliki transfer dalam rentang(-inf, 90 DAYS)Nadalah jumlah akun (a2) yang memiliki transfer dalam rentang[90 DAYS, 30 DAYS)Kadalah jumlah akun (a3) yang memiliki transfer dalam rentang[30 DAYS, +inf)
Kueri yang tidak dioptimalkan menghasilkan hasil perantara lebih awal, menghitung sub-jalur kedua
M kali dan sub-jalur ketiga M * N kali. Sebaliknya, versi terfaktorisasi mengoptimalkan eksekusi kueri dengan melakukan hal berikut:
Mendapatkan
Mnode tujuan (a1) untuk sub-jalur pertama, dan menyimpan sementara kumpulan nodea1.Mendapatkan
Nnode tujuan (a2) untuk sub-jalur kedua hanya satu kali dan menyimpan sementara kumpulan nodea2.Mendapatkan
Knode tujuan (a3) untuk sub-jalur terakhir hanya satu kali.Melakukan produk silang antara hasil perantara sementara untuk membuat produk dari
MxNxKcatatan.