Analitika velikih podataka s Neo4j i Javom, 1. dio

Relacijske baze podataka dominiraju upravljanjem podacima desetljećima, ali nedavno su izgubile tlo pod nogama nad NoSQL alternativama. Iako NoSQL spremišta podataka nisu prikladna za svaki slučaj upotrebe, oni su uglavnom bolji za velike podatke , što je skraćenica za sustave koji obrađuju velike količine podataka. Za velike podatke koriste se četiri vrste spremišta podataka:

  • Trgovine ključeva / vrijednosti kao što su Memcached i Redis
  • Baze podataka orijentirane na dokumente kao što su MongoDB, CouchDB i DynamoDB
  • Pohrane podataka orijentirane na stup kao što su Cassandra i HBase
  • Grafikujte baze podataka kao što su Neo4j i OrientDB

Ovaj vodič predstavlja Neo4j, bazu podataka grafova koja se koristi za interakciju s visoko povezanim podacima . Iako su relacijske baze podataka dobre u upravljanju odnosima između podataka, grafičke baze podataka bolje upravljaju n-tomodnosi stupnja. Kao primjer uzmimo društvenu mrežu na kojoj želite analizirati obrasce koji uključuju prijatelje, prijatelje prijatelja itd. Baza podataka grafikona olakšala bi odgovor na pitanje poput: "S obzirom na pet stupnjeva odvojenosti, što je pet filmova popularnih na mojoj društvenoj mreži koje još nisam vidio?" Takva su pitanja uobičajena za softver za preporuke, a baze podataka grafikona savršene su za njihovo rješavanje. Uz to, baze podataka grafikona dobro predstavljaju hijerarhijske podatke, kao što su kontrole pristupa, katalozi proizvoda, baze podataka filmova ili čak mrežne topologije i organizacijske karte. Kada imate objekte s višestrukim odnosima, brzo ćete otkriti da baze podataka grafikona nude elegantnu, objektno orijentiranu paradigmu za upravljanje tim objektima.

Slučaj za baze podataka grafova

Kao što im samo ime govori, baze podataka grafova dobro predstavljaju grafikone podataka. To je posebno korisno za društveni softver, gdje se svaki put kada se povežete s nekim definira odnos između vas. Vjerojatno ste u posljednjem traženju posla odabrali nekoliko tvrtki koje su vas zanimale, a zatim pretražili veze na njima na svojim društvenim mrežama. Iako možda ne znate nikoga tko radi u nekoj od tih tvrtki, netko na vašoj društvenoj mreži to vjerojatno zna. Rješavanje ovakvog problema lako je na jednom ili dva stupnja razdvajanja (vaš prijatelj ili prijatelj prijatelja), ali što se događa kada započnete proširivati ​​pretraživanje po vašoj mreži?

U svojoj knjizi Neo4j na djelu, Aleksa Vukotić i Nicki Watt istražuju razlike između relacijskih baza podataka i grafičkih baza podataka za rješavanje problema društvenih mreža. Iskoristit ću njihov rad za sljedećih nekoliko primjera kako bih vam pokazao zašto baze podataka s grafikonima postaju sve popularnija alternativa relacijskim bazama podataka.

Modeliranje složenih odnosa: Neo4j vs MySQL

Iz perspektive informatike, kad razmišljamo o modeliranju odnosa između korisnika na društvenoj mreži, mogli bismo nacrtati grafik poput onog na slici 1.

Steven Haines

Korisnik ima IS_FRIEND_OFodnose s drugim korisnicima, a ti korisnici imaju IS_FRIEND_OFveze s drugim korisnicima, i tako dalje. Slika 2 prikazuje kako bismo to predstavili u relacijskoj bazi podataka.

Steven Haines

USERTablica ima jedan-na-više odnos sa USER_FRIENDstola, koji modeli su „prijatelj” odnos između dva korisnika. Sad kad smo modelirali odnose, kako bismo postavili upit za svoje podatke? Vukotić i Watt mjerili su izvedbu upita za brojanje broja različitih prijatelja koji su izašli na dubinu od pet razina (prijatelji prijatelja prijatelja prijatelja prijatelja). U relacijskoj bazi podataka upiti bi izgledali kako slijedi:

 # 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... 

Zanimljivo je kod ovih upita da je svaki put kad izađemo na još jednu razinu, dužni pridružiti se USER_FRIENDstolu sa sobom. Tablica 1. pokazuje što su istraživači Vukotić i Watt pronašli kada su ubacili 1.000 korisnika s otprilike 50 odnosa (50.000 odnosa) i pokrenuli upite.

Tablica 1. Vrijeme odgovora na MySQL upit za različite dubine odnosa

DubinaIzvršenja (sekunde) Rezultat brojanja

2 0,028 ~ 900
3 0,213 ~ 999
4 10.273 ~ 999
5 92,613 ~ 999

MySQL sjajno radi spajanje podataka udaljenih do tri razine, ali performanse se nakon toga brzo degradiraju. Razlog je taj što svaki put kada se USER_FRIENDtablica spoji sama sa sobom, MySQL mora izračunati kartezijski proizvod tablice, iako će većina podataka biti bačena. Na primjer, pri izvođenju tog spajanja pet puta, kartezijanski proizvod rezultira s 50 000 ^ 5 redaka ili 102,4 * 10 ^ 21 redaka. To je gubitak kad nas zanima samo 1.000!

Dalje, Vukotić i Watt pokušali su izvršiti istu vrstu upita protiv Neo4j. Ovi potpuno različiti rezultati prikazani su u tablici 2.

Tablica 2. Vrijeme odziva Neo4j za različite dubine odnosa

DubinaIzvršenja (sekunde) Rezultat brojanja

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.

Nakon što instalirate Neo4j, pokrenite ga i otvorite prozor preglednika na sljedeći URL:

//127.0.0.1:7474/browser/

Prijavite se sa zadanim korisničkim imenom neo4ji zadanom lozinkom za neo4j. Trebali biste vidjeti zaslon sličan slici 3.

Steven Haines

Čvorovi i odnosi u Neo4j

Neo4j je dizajniran oko koncepta čvorova i odnosa:

  • Čvor predstavlja stvar, kao što je korisnik, film ili knjigu.
  • Čvor sadrži skup parova ključ / vrijednost , kao što su ime, naslov ili izdavač.
  • Čvor je naljepnica definira što tip što je - opet, korisnik, film ili knjigu.
  • Odnosi definiraju asocijacije između čvorova i imaju određene tipove.

Kao primjer, mogli bismo definirati čvorove znakova kao što su Iron Man i Captain America; definirati čvor filma pod nazivom "Osvetnici"; a zatim definirati APPEARS_INodnos između Iron Mana i Avengersa i Captain America i Avengersa. Sve je to prikazano na slici 4.

Steven Haines

Slika 4 prikazuje tri čvora (dva čvora s likovima i jedan čvor za film) i dva odnosa (oba tipa APPEARS_IN).

Modeliranje i ispitivanje čvorova i odnosa

Slično kao što relacijska baza podataka koristi strukturirani jezik upita (SQL) za interakciju s podacima, Neo4j koristi Cypher Query Language za interakciju s čvorovima i odnosima.

Upotrijebimo Cypher za stvaranje jednostavnog prikaza obitelji. Na vrhu web sučelja potražite znak dolara. To ukazuje na polje koje vam omogućuje izvršavanje Cypher upita izravno protiv Neo4j. U to polje unesite sljedeći Cypher-ov upit (koristim obitelj kao primjer, ali slobodno promijenite detalje kako biste oblikovali vlastitu obitelj ako želite):

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

Rezultat je prikazan na slici 5.

Steven Haines

Na slici 5 možete vidjeti novi čvor s oznakom Osoba i imenom Steven. Ako zadržite pokazivač miša iznad čvora na web-konzoli, vidjet ćete njegova svojstva na dnu. U ovom su slučaju svojstva ID: 19, ime: Steven i starost: 45. Sad ćemo razbiti upit 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

U ovom primjeru podudaramo se s dva čvora Person s oznakom Steven i Linda i stvaramo odnos tipa IS_MARRIED_TOod Stevena do Linde. Format za stvaranje odnosa je sljedeći:

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