Analisis data besar dengan Neo4j dan Java, Bagian 1

Basis data relasional telah mendominasi manajemen data selama beberapa dekade, tetapi mereka baru-baru ini kalah dari alternatif NoSQL. Meskipun penyimpanan data NoSQL tidak tepat untuk setiap kasus penggunaan, biasanya lebih baik untuk data besar , yang merupakan singkatan untuk sistem yang memproses data dalam jumlah besar. Empat jenis penyimpanan data digunakan untuk data besar:

  • Penyimpanan kunci / nilai seperti Memcached dan Redis
  • Database berorientasi dokumen seperti MongoDB, CouchDB, dan DynamoDB
  • Penyimpanan data berorientasi kolom seperti Cassandra dan HBase
  • Database grafik seperti Neo4j dan OrientDB

Tutorial ini memperkenalkan Neo4j, yang merupakan database grafik yang digunakan untuk berinteraksi dengan data yang sangat terkait . Sementara database relasional bagus dalam mengelola hubungan antar data, database grafik lebih baik dalam mengelola n-thhubungan derajat. Sebagai contoh, ambil jaringan sosial, di mana Anda ingin menganalisis pola yang melibatkan teman, teman dari teman, dan sebagainya. Database grafik akan memudahkan untuk menjawab pertanyaan seperti, "Dengan lima derajat pemisahan, apakah lima film populer di jejaring sosial saya yang belum pernah saya tonton?" Pertanyaan semacam itu umum untuk perangkat lunak rekomendasi, dan database grafik sangat cocok untuk menyelesaikannya. Selain itu, database grafik bagus dalam merepresentasikan data hierarki, seperti kontrol akses, katalog produk, database film, atau bahkan topologi jaringan dan bagan organisasi. Saat Anda memiliki objek dengan banyak hubungan, Anda akan segera menemukan bahwa database grafik menawarkan paradigma berorientasi objek yang elegan untuk mengelola objek tersebut.

Kasus untuk database grafik

Seperti namanya, database grafik bagus dalam merepresentasikan grafik data. Ini sangat berguna untuk perangkat lunak sosial, di mana setiap kali Anda terhubung dengan seseorang, hubungan ditentukan di antara Anda. Mungkin dalam pencarian kerja terakhir Anda, Anda memilih beberapa perusahaan yang Anda minati dan kemudian mencari koneksi ke jejaring sosial Anda. Meskipun Anda mungkin tidak mengenal siapa pun yang bekerja untuk salah satu perusahaan itu, seseorang di jejaring sosial Anda mungkin tahu. Memecahkan masalah seperti ini mudah dilakukan pada satu atau dua derajat pemisahan (teman Anda atau teman dari teman), tetapi apa yang terjadi ketika Anda mulai memperluas pencarian di seluruh jaringan Anda?

Dalam bukunya, Neo4j In Action, Aleksa Vukotic dan Nicki Watt mengeksplorasi perbedaan antara database relasional dan database grafik untuk memecahkan masalah jaringan sosial. Saya akan menggambar karya mereka untuk beberapa contoh berikutnya, untuk menunjukkan kepada Anda mengapa database grafik menjadi alternatif yang semakin populer untuk database relasional.

Pemodelan hubungan yang kompleks: Neo4j vs MySQL

Dari perspektif ilmu komputer, ketika kita memikirkan tentang pemodelan hubungan antara pengguna di jejaring sosial, kita mungkin menggambar grafik seperti yang ada di Gambar 1.

Steven Haines

Seorang pengguna memiliki IS_FRIEND_OFhubungan dengan pengguna lain, dan pengguna tersebut memiliki IS_FRIEND_OFhubungan dengan pengguna lain, dan seterusnya. Gambar 2 menunjukkan bagaimana kita merepresentasikan ini dalam database relasional.

Steven Haines

The USERtabel memiliki hubungan satu-ke-banyak dengan USER_FRIENDtabel, yang model hubungan "teman" antara dua pengguna. Sekarang setelah kita memodelkan hubungan, bagaimana kita akan membuat kueri data kita? Vukotic dan Watt mengukur kinerja query untuk menghitung jumlah teman berbeda yang keluar hingga kedalaman lima level (teman dari teman teman dari teman teman). Dalam database relasional, kueri akan terlihat seperti berikut:

 # Depth 1 select count(distinct uf.*) from user_friend uf where uf.user_1 = ? # Depth 2 select count(distinct uf2.*) from user_friend uf1 inner join user_friend uf2 on uf1.user_1 = uf2.user_2 where uf1.user_1 = ? # Depth 3 select count(distinct uf3.*) from t_user_friend uf1 inner join t_user_friend uf2 on uf1.user_1 = uf2.user_2 inner join t_user_friend uf3 on uf2.user_1 = uf3.user_2 where uf1.user_1 = ? # And so on... 

Yang menarik dari kueri ini adalah setiap kali kita keluar satu level lagi, kita diminta untuk menggabungkan USER_FRIENDtabel dengan tabel itu sendiri. Tabel 1 menunjukkan apa yang ditemukan peneliti Vukotic dan Watt ketika mereka memasukkan 1.000 pengguna dengan masing-masing sekitar 50 hubungan (50.000 hubungan) dan menjalankan kueri.

Tabel 1. Waktu respons kueri MySQL untuk berbagai kedalaman hubungan

DepthExecution time (detik) Hitung hasil

2 0,028 ~ 900
3 0.213 ~ 999
4 10.273 ~ 999
5 92.613 ~ 999

MySQL melakukan pekerjaan yang bagus untuk menggabungkan data hingga tiga tingkat, tetapi kinerja menurun dengan cepat setelah itu. Alasannya adalah bahwa setiap kali USER_FRIENDtabel digabungkan dengan tabel itu sendiri, MySQL harus menghitung produk kartesian dari tabel tersebut, meskipun sebagian besar data akan dibuang. Misalnya, saat menjalankan gabungan tersebut lima kali, hasil perkalian kartesius menghasilkan 50.000 ^ 5 baris, atau 102,4 * 10 ^ 21 baris. Sia-sia jika kita hanya tertarik pada 1.000 di antaranya!

Selanjutnya, Vukotic dan Watt mencoba mengeksekusi jenis kueri yang sama terhadap Neo4j. Hasil yang sama sekali berbeda ini ditunjukkan pada Tabel 2.

Tabel 2. Waktu respons Neo4j untuk berbagai kedalaman hubungan

DepthExecution time (detik) Hitung hasil

2 0,04 ~ 900
3 0,06 ~ 999
4 0,07 ~ 999
5 0,07 ~ 999

The takeaway from these execution comparisons is not that Neo4j is better than MySQL. Rather, when traversing these types of relationships, Neo4j's performance is dependent on the number of records retrieved, whereas MySQL's performance is dependent on the number of records in the USER_FRIEND table. Thus, as the number of relationships increases, the response times for MySQL queries will likewise increase, whereas the response times for Neo4j queries will remain the same. This is because Neo4j's response time is dependent on the number of relationships for a specific query, and not on the total number of relationships.

Scaling Neo4j for big data

Extending this thought project one step further, Vukotic and Watt next created a million users with 50 million relationships between them. Table 3 shows results for that data set.

Table 3. Neo4j response time for 50 million relationships

DepthExecution time (seconds)Count result

2 0.01 ~2,500
3 0.168 ~110,000
4 1.359 ~600,000
5 2.132 ~800,000

Needless to say, I am indebted to Aleksa Vukotic and Nicki Watt and highly recommend checking out their work. I extracted all the tests in this section from the first chapter of their book, Neo4j in Action.

Getting started with Neo4j

You've seen that Neo4j is capable of executing massive amounts of highly related data very quickly, and there's no doubt it's a better fit than MySQL (or any relational database) for certain kinds of problems. If you want to understand more about how Neo4j works, the easiest way is to interact with it through the web console.

Start by downloading Neo4j. For this article, you'll want the Community Edition, which as of this writing is at version 3.2.3.

  • On a Mac, download a DMG file and install it as you would any other application.
  • On Windows, either download an EXE and walk through an installation wizard or download a ZIP file and decompress it on your hard drive.
  • On Linux, download a TAR file and decompress it on your hard drive.
  • Alternatively, use a Docker image on any operating system.

Setelah Anda menginstal Neo4j, mulailah dan buka jendela browser ke URL berikut:

//127.0.0.1:7474/browser/

Login dengan nama pengguna default neo4jdan kata sandi default neo4j. Anda akan melihat layar seperti Gambar 3.

Steven Haines

Node dan hubungan di Neo4j

Neo4j dirancang dengan konsep node dan hubungan:

  • Sebuah simpul mewakili hal, seperti pengguna, film, atau buku.
  • Node berisi sekumpulan pasangan kunci / nilai , seperti nama, judul, atau penerbit.
  • Label node mendefinisikan jenis barang itu - sekali lagi, Pengguna, Film, atau Buku.
  • Hubungan menentukan asosiasi antara node dan jenis tertentu.

Sebagai contoh, kita mungkin mendefinisikan node Karakter seperti Iron Man dan Captain America; tentukan node Film bernama "Avengers"; dan kemudian menentukan APPEARS_INhubungan antara Iron Man dan Avengers dan Captain America dan Avengers. Semua ini ditunjukkan pada Gambar 4.

Steven Haines

Gambar 4 menunjukkan tiga node (dua node Karakter dan satu node Film) dan dua hubungan (keduanya tipe APPEARS_IN).

Pemodelan dan kueri node dan hubungan

Mirip dengan bagaimana database relasional menggunakan Structured Query Language (SQL) untuk berinteraksi dengan data, Neo4j menggunakan Bahasa Query Cypher untuk berinteraksi dengan node dan hubungan.

Mari gunakan Cypher untuk membuat representasi sederhana dari sebuah keluarga. Di bagian atas antarmuka web, cari tanda dolar. Ini menunjukkan bidang yang memungkinkan Anda mengeksekusi kueri Cypher langsung terhadap Neo4j. Masukkan kueri Cypher berikut ke dalam bidang itu (saya menggunakan keluarga saya sebagai contoh, tetapi jangan ragu untuk mengubah detailnya menjadi model keluarga Anda sendiri jika Anda mau):

CREATE (person:Person {name: "Steven", age: 45}) RETURN person

Hasilnya ditunjukkan pada Gambar 5.

Steven Haines

Pada Gambar 5 Anda dapat melihat node baru dengan label Person dan nama Steven. Jika Anda mengarahkan mouse ke node di konsol web Anda, Anda akan melihat propertinya di bagian bawah. Dalam hal ini, propertinya adalah ID: 19, name: Steven, dan age: 45. Sekarang mari kita uraikan kueri Cypher:

  • CREATE: The CREATE keyword is used to create nodes and relationships. In this case, we pass it a single argument, which is a Person enclosed in parentheses, so it is meant to create a single node.
  • (person: Person {...}): The lower case "person" is a variable name through which we can access the person being created, while the capital "Person" is the label. Note that a colon separates the variable name from the label.
  • {name: "Steven, age: 45}: These are the key/value properties that we're defining for the node we're creating. Neo4j does not require you to define a schema before creating nodes and each node can have a unique set of elements. (Most of the time you define nodes with the same label to have the same properties, but it is not required.)
  • RETURN person: After the node is created, we ask Neo4j to return it back to us. This is why we saw the node appear in the user interface.

The CREATE command (which is case insensitive) is used to create nodes and can be read as follows: create a new node with the Person label that contains name and age properties; assign it to the person variable and return it back to the caller.

Querying with Cypher Query Language

Next we want to try some querying with Cypher. First, we'll need to create a few more people, so that we can define relationships between them.

 CREATE (person:Person {name: "Michael", age: 16}) RETURN person CREATE (person:Person {name: "Rebecca", age: 7}) RETURN person CREATE (person:Person {name: "Linda"}) RETURN person 

Once you've created your four people, you can either click on the Person button under the Node Labels (visible if you click on the database icon in the upper left corner of the web page) or execute the following Cypher query:

MATCH (person: Person) RETURN person

Cypher uses the MATCH keyword to find things in Neo4j. In this example, we are asking Cypher to match all nodes that have a label of Person, assign those nodes to the person variable, and return the value that is associated with that variable. As a result you should see the four nodes that you've created. If you hover over each node in your web console, you will see each person's properties. (You might note that I excluded my wife's age from her node, illustrating that properties do not need to be consistent across nodes, even of the same label. I am also not foolish enough to publish my wife's age.)

We can extends this MATCH example a little further by adding conditions to the nodes we want returned. For example, if we wanted just the "Steven" node, we could retrieve it by matching on the name property:

MATCH (person: Person {name: "Steven"}) RETURN person

Or, if we wanted to return all of the children we could request all people having an age under 18:

MATCH (person: Person) WHERE person.age < 18 RETURN person

In this example we added the WHERE clause to the query to narrow our results. WHERE works very similarly to its SQL equivalent: MATCH (person: Person) finds all nodes with the Person label, and then the WHERE clause filters values out of the result set.

Modeling direction in relationships

We have four nodes, so let's create some relationships. First of all, let's create the IS_MARRIED_TO relationship between Steven and Linda:

MATCH (steven:Person {name: "Steven"}), (linda:Person {name: "Linda"}) CREATE (steven)-[:IS_MARRIED_TO]->(linda) return steven, linda

Dalam contoh ini kami mencocokkan dua node Person berlabel Steven dan Linda, dan kami membuat hubungan tipe IS_MARRIED_TOdari Steven ke Linda. Format pembuatan relasi adalah sebagai berikut:

(node1)-[relationshipVariable:RELATIONSHIP_TYPE->(node2)