Praktik terbaik untuk menyesuaikan kueri Grafik Spanner

Dokumen ini menjelaskan praktik terbaik untuk menyesuaikan performa kueri Spanner Graph, yang mencakup pengoptimalan berikut:

  • Hindari pemindaian penuh tabel input untuk node dan tepi.
  • Kurangi jumlah data yang perlu dibaca kueri dari penyimpanan.
  • Kurangi ukuran data perantara.

Mulai dari node kardinalitas yang lebih rendah

Tulis traversal jalur sehingga dimulai dengan node kardinalitas yang lebih rendah. Pendekatan ini menjaga agar set hasil perantara tetap kecil, dan mempercepat eksekusi kueri.

Misalnya, kueri berikut memiliki semantik yang sama:

  • Traversal tepi depan:

    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 tepi terbalik:

    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 jumlah orang dengan nama Alex lebih sedikit daripada jumlah akun yang diblokir, sebaiknya tulis kueri ini dalam penelusuran tepi maju.

Memulai dari node kardinalitas yang lebih rendah sangat penting untuk penelusuran jalur 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 tidak ada. Sebaiknya tentukan label untuk semua node dan tepi jika memungkinkan, karena inferensi ini mungkin tidak selalu dapat dilakukan dan dapat menyebabkan lebih banyak label daripada yang diperlukan dipindai.

Pernyataan MATCH tunggal

Contoh berikut menemukan akun yang ditautkan dengan maksimal 3 transfer dari akun tertentu:

GRAPH FinGraph
MATCH (src:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id;

Di seluruh pernyataan MATCH

Tentukan label pada node dan tepi saat merujuk pada 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 tepi dan membatasi penelusuran 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 berbagai ukuran sampel untuk menemukan keseimbangan optimal antara data yang ditampilkan dan peningkatan performa kueri.

Contoh IS_FIRST ini menggunakan FinGraph, grafik keuangan dengan Account node dan Transfers tepi 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 mengkueri grafik, beberapa node dapat memiliki jumlah tepi masuk atau keluar yang jauh lebih besar dibandingkan dengan node lainnya. Node kardinalitas tinggi ini terkadang disebut sebagai node super atau node hub. Node super dapat menyebabkan masalah performa karena penelusuran 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 tepi yang dilalui kueri dari node. Karena akun di FinGraph mungkin memiliki jumlah transaksi yang jauh lebih tinggi daripada akun lainnya, Anda dapat menggunakan IS_FIRST untuk mencegah kueri yang tidak efisien. Teknik ini sangat berguna saat Anda tidak memerlukan enumerasi lengkap semua koneksi dari super node.

Kueri berikut menemukan akun (a2) yang menerima transfer secara langsung atau tidak langsung dari akun yang diblokir (a1). Kueri ini menggunakan IS_FIRST untuk mencegah performa lambat saat akun memiliki banyak transfer dengan membatasi jumlah edge Transfers yang perlu 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:

  • @max_transfers_per_account: Parameter kueri yang menentukan jumlah maksimum tepi Transfers yang akan dipertimbangkan untuk setiap akun (a1).

  • PARTITION BY SOURCE_NODE_ID(selected_e): Memastikan batas IS_FIRST diterapkan secara terpisah untuk setiap akun (a1).

  • ORDER BY selected_e.create_time DESC: Menentukan bahwa transfer terbaru akan ditampilkan.

Contoh 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, pecah 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 pernyataan MATCH pertama.

  • Untuk setiap node akun perantara (a2), IS_FIRST hanya mempertimbangkan tepi Transfers masuk pertama (e1), sehingga mengurangi jumlah jalur yang harus dieksplorasi dalam pernyataan MATCH kedua.

  • Efisiensi kueri dua hop secara keseluruhan ditingkatkan karena MATCH kedua tidak memproses data yang tidak diperlukan, terutama saat a2 memiliki banyak transfer masuk.

Langkah berikutnya