Bentuk pola pada kueri Cypher menentukan urutan kerja mesin, karena planner, yaitu komponen yang menyusun rencana eksekusi kueri, menerjemahkan pola yang ditulis menjadi rangkaian langkah yang dikerjakannya. Topik ini membahas cara berpikir planner, yaitu bagaimana pola MATCH dieksekusi tahap demi tahap, mengapa titik jangkar (anchoring) yang selektif memperkecil ruang kerja, dan bagaimana dua pola MATCH yang tidak terhubung menghasilkan produk Kartesius (Cartesian product). Dua kueri dengan bentuk pola berbeda dibandingkan pada dataset 10000 artikel menggunakan PROFILE, dengan fokus pada angka db hits, yakni hitungan operasi baca dan tulis mesin, dan jumlah barisnya. Topik ditutup dengan kebiasaan menulis kueri yang menjaga rencana tetap efisien.
Cara planner mengeksekusi pola#
Planner menerjemahkan setiap pola MATCH menjadi rangkaian operator yang dikerjakan berurutan. Operator pertama menghasilkan baris-baris awal, kemudian setiap operator berikutnya menerima baris dari tahap sebelumnya dan menghasilkan baris baru, misalnya dengan menelusuri relasi dari setiap node yang diterimanya. Jumlah baris yang mengalir pada satu titik rencana merupakan ruang kerja kueri pada titik itu, dan biaya keseluruhan tumbuh mengikuti jumlah baris yang diproses pada setiap tahap. Pembacaan operator baris demi baris dibahas pada topik rencana kueri, sehingga pembahasan di sini cukup menyusuri aliran barisnya.
Pola yang dimulai dari kumpulan besar membawa banyak baris ke seluruh tahap berikutnya. Pemindaian label :Artikel menghasilkan 10000 baris sebelum satu saringan pun bekerja, sedangkan pencarian indeks pada properti slug menghasilkan satu baris. Node pertama yang dicocokkan disebut titik jangkar, dan jangkar yang selektif, yakni menyisakan sedikit kandidat, memperkecil ruang kerja sejak tahap pertama sehingga seluruh tahap sesudahnya bekerja pada kumpulan yang lebih kecil. Saringan yang diletakkan pada jangkar juga bekerja lebih awal daripada saringan yang baru diterapkan setelah pola membesar.
Dua MATCH yang tidak terhubung#
Dua pola MATCH yang tidak berbagi variabel tidak dapat digabungkan menjadi satu penelusuran. Mesin mengeksekusi kedua pola secara terpisah, kemudian memasangkan setiap baris dari pola pertama dengan seluruh baris dari pola kedua. Bentuk penggabungan ini disebut produk Kartesius, dengan operator CartesianProduct yang umumnya berkinerja buruk dan sebaiknya dihindari. Jumlah barisnya m × k, dengan m baris dari pola pertama dan k baris dari pola kedua.
Kueri berikut menanyakan tag milik satu artikel, tetapi polanya ditulis sebagai dua MATCH yang berdiri sendiri.
PROFILE MATCH (t:Tag)
MATCH (a:Artikel)
WHERE a.slug = 'artikel-500'
RETURN t.name AS tag, a.slug AS slug;tag, slug
"graf", "artikel-500"
"basis-data", "artikel-500"
"cypher", "artikel-500"
Plan: "PROFILE"
Statement: "READ_ONLY"
Version: "25"
Planner: "COST"
Runtime: "SLOTTED"
Time: 5
DbHits: 9
Rows: 3
Memory (Bytes): 64Keluarannya tiga baris, satu untuk setiap tag pada graf, padahal artikel-500 hanya terhubung ke satu tag melalui relasi :BERTAG. Tiga baris itu adalah hasil perkalian 3 × 1, sehingga kueri ini keliru secara logika, bukan sekadar lambat. Mesin sebenarnya tetap mencari artikel melalui indeks, yang tercermin dari DbHits 9 yang masih kecil pada contoh kecil ini. Yang harus dibaca adalah bentuk perkaliannya, yaitu Rows 3 untuk jawaban yang seharusnya satu baris.
Ruang kerja perkalian tumbuh mengikuti perkalian ukuran kedua sisinya. Kueri berikut memperbesar sisi pertama menjadi seluruh artikel terbitan 2015, sebanyak 1000 artikel, lalu menghitungnya dengan count(*) agar keluarannya tetap ringkas, disertai pembanding tanpa MATCH kedua.
PROFILE MATCH (t:Tag)
MATCH (a:Artikel)
WHERE a.tahun = 2015
RETURN count(*) AS baris;
PROFILE MATCH (a:Artikel)
WHERE a.tahun = 2015
RETURN count(*) AS baris;baris
3000
Plan: "PROFILE"
Statement: "READ_ONLY"
Version: "25"
Planner: "COST"
Runtime: "SLOTTED"
Time: 7
DbHits: 24001
Rows: 1
Memory (Bytes): 88
baris
1000
Plan: "PROFILE"
Statement: "READ_ONLY"
Version: "25"
Planner: "COST"
Runtime: "SLOTTED"
Time: 6
DbHits: 20001
Rows: 1
Memory (Bytes): 88Hasil hitung kueri pertama 3000, yaitu 1000 artikel dikali tiga tag, sedangkan kueri pembandingnya menghasilkan 1000. DbHits naik dari 20001 menjadi 24001 karena 3000 baris gabungan diproses hingga agregasi selesai. Kedua kueri mengembalikan satu baris angka, sehingga biaya tambahan itu tidak terlihat pada ukuran keluarannya, hanya pada DbHits.
Jangkar selektif pada node terindeks#
Kueri berikut mengajukan pertanyaan yang sama dengan kueri pertama, yaitu tag milik artikel-500, tetapi polanya ditulis sebagai satu jalur yang terhubung. Titik jangkarnya adalah node :Artikel yang dicari melalui properti slug, yaitu properti yang didukung oleh constraint keunikan, kemudian relasi :BERTAG ditelusuri dari jangkar tersebut.
PROFILE MATCH (a:Artikel {slug: 'artikel-500'})-[:BERTAG]->(t:Tag)
RETURN a.slug AS slug, t.name AS tag;slug, tag
"artikel-500", "basis-data"
Plan: "PROFILE"
Statement: "READ_ONLY"
Version: "25"
Planner: "COST"
Runtime: "SLOTTED"
Time: 25
DbHits: 6
Rows: 1
Memory (Bytes): 64Mesin mencari satu artikel melalui indeks terlebih dahulu, lalu menelusuri relasi :BERTAG dari titik itu, sehingga tidak ada penggabungan antarpola. Rows bernilai 1 dan DbHits 6, sedangkan bentuk terpisah mencatat 3 baris dan 9 db hits pada dataset yang sama. Angka pada kedua bentuk hanya sedikit berbeda karena contohnya sengaja dibuat kecil, tetapi arahnya konsisten, yaitu lebih sedikit baris dan pembacaan. Yang lebih penting, jawabannya benar, yaitu satu baris sesuai isi graf, bukan tiga baris hasil perkalian.
Perbandingan kedua bentuk pada dataset yang sama dirangkum pada tabel berikut.
| Bentuk pola | DbHits | Baris hasil |
|---|---|---|
Dua MATCH terpisah dengan saringan slug | 9 | 3 |
Satu pola terhubung berjangkar slug | 6 | 1 |
Posisi jangkar dalam teks kueri bukan perintah yang diikuti planner secara kaku. Pada satu pola MATCH yang terhubung, planner menata ulang langkah berdasarkan biaya, sehingga varian yang menempatkan predikat yang sama di WHERE setelah pola menghasilkan angka yang identik pada pengukuran ini. Kebiasaan menulis jangkar selektif lebih dahulu tetap berguna karena membuat maksud kueri terbaca dan menghindarkan pola dari struktur yang memaksa perkalian, yaitu dua MATCH yang tidak berbagi variabel.
Kebiasaan menulis pola kueri#
Kebiasaan yang menjaga rencana tetap efisien dirangkum menjadi tiga aturan menulis. Pertama, tulis satu pola yang terhubung, yaitu setiap bagiannya tersambung ke bagian lain melalui variabel atau relasi, sehingga mesin menelusuri alih-alih mengalikan. Kebutuhan yang memang tidak berhubungan, misalnya mengambil daftar tag untuk tajuk halaman dan satu artikel untuk isinya, sebaiknya dipecah menjadi dua kueri. Kedua, mulai dari jangkar yang selektif, yaitu node yang jumlah kandidatnya sedikit, dan letakkan node itu di awal pola. Ketiga, saring pada properti yang terindeks, karena saringan itulah yang membuat jangkar langsung menyisakan sedikit baris, sebagaimana slug pada contoh-contoh sebelumnya.
Ketiga aturan itu saling menopang, karena jangkar selektif baru bekerja apabila ada saringan terindeks yang mendukungnya, dan pola yang terhubung menjadikan hasil jangkar sebagai titik awal penelusuran alih-alih salah satu sisi perkalian. Setelah kueri ditulis, PROFILE memverifikasi asumsinya pada data yang representatif, karena planner memilih rencana berdasarkan statistik data yang tersimpan.
Kesalahan umum#
Tiga kesalahan berikut paling sering muncul saat topik ini dipraktikkan.
- Menggabungkan dua kebutuhan yang tidak berhubungan dalam satu kueri. Dua
MATCHtanpa variabel penghubung menghasilkan perkalian baris sebagaimana kueri pertama pada topik ini, dan barisnya menyesatkan karena memasangkan node yang tidak berhubungan. Perbaikannya adalah menghubungkan pola melalui relasi atau memecahnya menjadi dua kueri yang masing-masing berjangkar. - Menilai kualitas rencana hanya dari ukuran keluaran. Pada kueri
count(*)kedua, keluarannya satu angka, sedangkan ruang kerja di tengah rencana sudah mencapai 3000 baris danDbHitsnaik dari 20001 menjadi 24001. Perbaikannya adalah membacaDbHitsdan membandingkan dua bentuk kueri pada data yang sama. - Menjangkar pada properti yang tidak terindeks. Pola yang terhubung tidak banyak menolong apabila titik jangkarnya memicu pemindaian seluruh label, misalnya saringan pada properti yang tidak memiliki indeks. Perbaikannya adalah memilih properti terindeks untuk jangkar, atau memasang indeks pada properti yang menjadi saringan rutin sebagaimana dibahas pada topik indeks.
Ringkasan#
- Planner mengeksekusi pola
MATCHtahap demi tahap, dan baris yang dihasilkan tahap awal dibawa ke seluruh tahap berikutnya, sehingga titik jangkar yang selektif memperkecil biaya keseluruhan. - Dua pola
MATCHyang tidak berbagi variabel menghasilkan produk Kartesius, yaitu perkalian m × k baris, dan bentuk ini pada dataset contoh mencatat 9 db hits untuk tiga baris yang keliru secara logika. - Satu pola terhubung berjangkar pada
slugmenjawab pertanyaan yang sama dengan 6 db hits untuk satu baris yang benar, dan planner menata ulang langkah di dalam satu pola terhubung tanpa perlu diarahkan. - Tiga kebiasaan yang menjaga rencana tetap efisien adalah satu pola terhubung, jangkar selektif lebih dahulu, dan saringan pada properti terindeks, dengan
PROFILEsebagai alat pemeriksaannya.
Referensi#
- Query tuning — Neo4j Docs — Rekomendasi umum planner Cypher, termasuk saran menyaring data sedini mungkin untuk mengurangi pekerjaan tahap akhir.
- Execution plans — Neo4j Docs
— Menjelaskan pembentukan rencana eksekusi dan cara membaca operator pada keluaran
PROFILE. - Operators in detail — Neo4j Docs
— Deskripsi operator rencana eksekusi, termasuk
CartesianProductyang menggabungkan setiap baris kedua sisinya.