Programmering

Big data-analyse med Neo4j og Java, del 1

Relasjonsdatabaser har dominert datahåndtering i flere tiår, men de har nylig mistet terreng for NoSQL-alternativer. Mens NoSQL-datalagre ikke passer for enhver brukstilfelle, er de generelt bedre for stor Data, som er stenografi for systemer som behandler store datamengder. Fire typer datalager brukes til store data:

  • Nøkkel- / verdibutikker som Memcached og Redis
  • Dokumentorienterte databaser som MongoDB, CouchDB og DynamoDB
  • Kolonneorienterte datalagre som Cassandra og HBase
  • Grafdatabaser som Neo4j og OrientDB

Denne opplæringen introduserer Neo4j, som er en grafdatabase som brukes til å samhandle med svært relaterte data. Mens relasjonsdatabaser er gode til å håndtere relasjoner mellom data, grafdatabaser er bedre til å administrere n-th grad relasjoner. Ta et sosialt nettverk der du vil analysere mønstre som involverer venner, venner av venner og så videre. En grafdatabase ville gjøre det enkelt å svare på et spørsmål som: "Gitt fem grader av separasjon, hva er fem filmer populære blant mitt sosiale nettverk som jeg ennå ikke har sett?" Slike spørsmål er vanlige for anbefalingsprogramvare, og grafdatabaser er perfekte for å løse dem. I tillegg er grafdatabaser gode til å representere hierarkiske data, for eksempel tilgangskontroller, produktkataloger, filmdatabaser eller til og med nettverkstopologier og organisasjonskart. Når du har objekter med flere forhold, vil du raskt oppdage at grafdatabaser tilbyr et elegant, objektorientert paradigme for å administrere disse objektene.

Saken for grafdatabaser

Som navnet antyder, er grafdatabaser flinke til å representere datagrafer. Dette er spesielt nyttig for sosial programvare, der hver gang du kontakter noen, er det definert et forhold mellom deg. Sannsynligvis i det siste jobbsøket ditt, valgte du noen selskaper du var interessert i, og deretter søkte du i dine sosiale nettverk for tilkoblinger til dem. Selv om du kanskje ikke kjenner noen som jobber for et av disse selskapene, gjør det sannsynligvis noen i ditt sosiale nettverk. Å løse et problem som dette er enkelt i en eller to grader av separasjon (din venn eller en venn av en venn), men hva skjer når du begynner å utvide søket i nettverket ditt?

I sin bok, Neo4j In Action, utforsker Aleksa Vukotic og Nicki Watt forskjellene mellom relasjonsdatabaser og grafdatabaser for å løse sosiale nettverksproblemer. Jeg kommer til å trekke på arbeidet deres for de neste eksemplene, for å vise deg hvorfor grafdatabaser blir et stadig mer populært alternativ til relasjonsdatabaser.

Modellering av komplekse forhold: Neo4j vs MySQL

Fra et informatikkperspektiv, når vi tenker modelleringsforhold mellom brukere i et sosialt nettverk, kan vi tegne en graf som den i figur 1.

Steven Haines

En bruker har IS_FRIEND_OF forhold til andre brukere, og disse brukerne har IS_FRIEND_OF forhold til andre brukere, og så videre. Figur 2 viser hvordan vi vil representere dette i en relasjonsdatabase.

Steven Haines

De BRUKER tabellen har et en-til-mange forhold til USER_FRIEND tabell, som modellerer "venn" -forholdet mellom to brukere. Nå som vi har modellert forholdene, hvordan vil vi spørre om dataene våre? Vukotic og Watt målte spørringsytelsen for å telle antall forskjellige venner som gikk ut til en dybde på fem nivåer (venner av venner av venner av venner av venner). I en relasjonsdatabase vil spørsmålene se slik ut:

 # Dybde 1 velg antall (distinkt uf. *) Fra user_friend uf der uf.user_1 =? # Dybde 2 velg antall (distinkt uf2. *) Fra user_friend uf1 indre join user_friend uf2 på uf1.user_1 = uf2.user_2 hvor uf1.user_1 =? # Dybde 3 velg antall (distinkt uf3. *) Fra t_user_friend uf1 indre join t_user_friend uf2 på uf1.user_1 = uf2.user_2 indre join t_user_friend uf3 på uf2.user_1 = uf3.user_2 hvor uf1.user_1 =? # Og så videre... 

Det som er interessant med disse spørsmålene, er at hver gang vi går ut ett nivå til, er vi pålagt å bli med i USER_FRIEND bord med seg selv. Tabell 1 viser hva forskerne Vukotic og Watt fant da de satte inn 1000 brukere med omtrent 50 relasjoner hver (50 000 relasjoner) og kjørte spørsmålene.

Tabell 1. MySQL-spørretid for forskjellige dybder av forhold

DepthExecution time (seconds) Count result

20.028~900
30.213~999
410.273~999
592.613~999

MySQL gjør en god jobb med å bli med data opptil tre nivåer unna, men ytelsen forringes raskt etter det. Årsaken er at hver gang USER_FRIEND tabellen er forbundet med seg selv, må MySQL beregne det kartesiske produktet av tabellen, selv om flertallet av dataene blir kastet. For eksempel når du utfører den sammenføyningen fem ganger, resulterer det kartesiske produktet i 50 000 ^ 5 rader, eller 102,4 * 10 ^ 21 rader. Det er bortkastet når vi bare er interessert i 1000 av dem!

Deretter prøvde Vukotic og Watt å utføre samme type spørsmål mot Neo4j. Disse helt forskjellige resultatene er vist i tabell 2.

Tabell 2. Neo4j responstid for ulike dybder av forhold

DepthExecution time (seconds) Count result

20.04~900
30.06~999
40.07~999
50.07~999

Takeaway fra disse sammenligningene er ikke at Neo4j er bedre enn MySQL. Snarere, når du krysser denne typen relasjoner, er Neo4js ytelse avhengig av antall poster som er hentet, mens MySQLs ytelse er avhengig av antall poster i USER_FRIEND bord. Når antall relasjoner øker, vil responstidene for MySQL-spørsmål også øke, mens responstidene for Neo4j-spørsmål vil forbli de samme. Dette er fordi Neo4js responstid er avhengig av antall relasjoner for et bestemt spørsmål, og ikke av det totale antallet relasjoner.

Skalerer Neo4j for store data

Ved å utvide dette tankeprosjektet et skritt videre, skapte Vukotic og Watt deretter en million brukere med 50 millioner forhold mellom seg. Tabell 3 viser resultater for det datasettet.

Tabell 3. Neo4j responstid for 50 millioner relasjoner

DepthExecution time (seconds) Count result

20.01~2,500
30.168~110,000
41.359~600,000
52.132~800,000

Det er unødvendig å si at jeg har gjeld til Aleksa Vukotic og Nicki Watt og anbefaler på det sterkeste å sjekke ut arbeidet deres. Jeg hentet alle testene i denne delen fra det første kapittelet i boken deres, Neo4j i aksjon.

Komme i gang med Neo4j

Du har sett at Neo4j er i stand til å utføre store mengder svært relaterte data veldig raskt, og det er ingen tvil om at det passer bedre enn MySQL (eller en hvilken som helst relasjonsdatabase) for visse typer problemer. Hvis du vil forstå mer om hvordan Neo4j fungerer, er den enkleste måten å samhandle med den via nettkonsollen.

Start med å laste ned Neo4j. For denne artikkelen vil du ha Community Edition, som i skrivende stund er i versjon 3.2.3.

  • Last ned en DMG-fil på en Mac og installer den som du ville gjort med andre applikasjoner.
  • På Windows kan du enten laste ned en EXE og gå gjennom en installasjonsveiviser eller laste ned en ZIP-fil og dekomprimere den på harddisken.
  • På Linux laster du ned en TAR-fil og dekomprimerer den på harddisken din.
  • Alternativt kan du bruke et Docker-bilde på ethvert operativsystem.

Når du har installert Neo4j, starter du den og åpner et nettleservindu for følgende URL:

//127.0.0.1:7474/browser/

Logg inn med standard brukernavn for neo4j og standardpassordet til neo4j. Du bør se en skjerm som ligner på figur 3.

Steven Haines

Noder og relasjoner i Neo4j

Neo4j er designet rundt begrepet noder og relasjoner:

  • EN node representerer en ting, for eksempel en bruker, en film eller en bok.
  • En node inneholder et sett med nøkkel / verdipar, for eksempel et navn, en tittel eller en forlegger.
  • En node merkelapp definerer hvilken type ting det er - igjen, en bruker, en film eller en bok.
  • Forhold definere assosiasjoner mellom noder og er av spesifikke typer.

Som et eksempel kan vi definere tegnnoder som Iron Man og Captain America; definere en filmknute som heter "Avengers"; og definer deretter en APPEARS_IN forholdet mellom Iron Man og Avengers og Captain America og Avengers. Alt dette er vist i figur 4.

Steven Haines

Figur 4 viser tre noder (to tegnnoder og en filmnode) og to relasjoner (begge av typen APPEARS_IN).

Modellering og spørring av noder og relasjoner

På samme måte som en relasjonsdatabase bruker Structured Query Language (SQL) for å samhandle med data, bruker Neo4j Cypher Query Language for å samhandle med noder og relasjoner.

La oss bruke Cypher til å lage en enkel fremstilling av en familie. På toppen av webgrensesnittet, se etter dollartegnet. Dette indikerer et felt som lar deg utføre Cypher-spørsmål direkte mot Neo4j. Skriv inn følgende Cypher-spørsmål i det feltet (jeg bruker familien min som et eksempel, men endre gjerne detaljene for å modellere din egen familie hvis du vil):

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

Resultatet er vist i figur 5.

Steven Haines

I figur 5 kan du se en ny node med etiketten Person og navnet Steven. Hvis du holder musen over noden i nettkonsollen din, vil du se dens egenskaper nederst. I dette tilfellet er eiendommene ID: 19, navn: Steven, og alder: 45. La oss nå dele ned Cypher-spørringen:

  • SKAPE: The SKAPE nøkkelord brukes til å lage noder og relasjoner. I dette tilfellet gir vi det et enkelt argument, som er en Person lukket i parentes, så det er ment å opprette en enkelt node.
  • (person: Person {...}): Små bokstaver "person"er et variabelt navn som vi får tilgang til personen som blir opprettet, mens hovedstaden"Person"er etiketten. Merk at et kolon skiller variabelnavnet fra etiketten.
  • {name: "Steven, alder: 45}: Dette er nøkkel- / verdiegenskapene vi definerer for noden vi oppretter. Neo4j krever ikke at du definerer et skjema før du oppretter noder, og hver node kan ha et unikt sett med elementer. (Mesteparten av tiden definerer du noder med samme etikett for å ha de samme egenskapene, men det er ikke nødvendig.)
  • RETUR person: Etter at noden er opprettet, ber vi Neo4j om å returnere den tilbake til oss. Dette er grunnen til at vi så noden vises i brukergrensesnittet.

De SKAPE kommando (som ikke skiller mellom store og små bokstaver) brukes til å lage noder og kan leses som følger: lage en ny node med Person-etiketten som inneholder navn og aldersegenskaper; tilordne den til personvariabelen og returnere den tilbake til den som ringer.

Spørring med Cypher Query Language

Deretter vil vi prøve å spørre med Cypher. Først må vi opprette noen flere mennesker, slik at vi kan definere forholdet mellom dem.

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

Når du har opprettet dine fire personer, kan du enten klikke på Person knappen under Node-etiketter (synlig hvis du klikker på databaseikonet øverst til venstre på websiden) eller utfører følgende Cypher-spørring:

KAMP (person: Person) RETUR person

Cypher bruker KAMP nøkkelord for å finne ting i Neo4j. I dette eksemplet ber vi Cypher om å matche alle noder som har en personmerke, tildele disse noder til person variabel, og returner verdien som er knyttet til den variabelen. Som et resultat bør du se de fire nodene du har opprettet. Hvis du holder markøren over hver node i nettkonsollen din, vil du se hver persons egenskaper. (Du kan legge merke til at jeg ekskluderte min kone alder fra noden hennes, noe som illustrerer at egenskapene ikke trenger å være konsistente på tvers av noder, til og med med samme etikett. Jeg er heller ikke tåpelig nok til å publisere konas alder.)

Vi kan utvide dette KAMP eksempel litt lenger ved å legge til betingelser i nodene vi vil returnere. Hvis vi for eksempel bare ville ha "Steven" -noden, kunne vi hente den ved å matche på navnegenskapen:

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

Eller hvis vi ønsket å returnere alle barna, kan vi be alle mennesker under 18 år:

KAMP (person: Person) HVOR person. Alder <18 RETUR person

I dette eksemplet la vi til HVOR klausul til spørringen for å begrense resultatene våre. HVOR fungerer veldig likt SQL-ekvivalenten: MATCH (person: person) finner alle noder med Person-etiketten, og deretter HVOR klausul filtrerer verdier ut av resultatsettet.

Modelleringsretning i forhold

Vi har fire noder, så la oss lage noen relasjoner. Først av alt, la oss lage IS_MARRIED_TO forholdet mellom Steven og Linda:

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

I dette eksemplet samsvarer vi med to personnoder merket Steven og Linda, og vi skaper et forhold av typen IS_MARRIED_TO fra Steven til Linda. Formatet for å skape forholdet er som følger:

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