Programmering

Big data-analyse med Neo4j og Java, del 1

Relationsdatabaser har domineret datastyring i årtier, men de har for nylig mistet terrænet for NoSQL-alternativer. Mens NoSQL-datalagre ikke passer til enhver brugssag, er de generelt bedre til store data, som er stenografi for systemer, der behandler store datamængder. Fire typer datalager bruges til big data:

  • Nøgle- / værdibutikker som Memcached og Redis
  • Dokumentorienterede databaser som MongoDB, CouchDB og DynamoDB
  • Søjleorienterede datalagre som Cassandra og HBase
  • Grafdatabaser som Neo4j og OrientDB

Denne tutorial introducerer Neo4j, som er en grafdatabase, der bruges til at interagere med stærkt relaterede data. Mens relationsdatabaser er gode til at styre relationer mellem data, grafdatabaser er bedre til at styre n-th grad forhold. Tag et eksempel på et socialt netværk, hvor du vil analysere mønstre, der involverer venner, venners venner osv. En grafdatabase ville gøre det let at besvare et spørgsmål som: "I betragtning af fem grader af adskillelse, hvad er fem film populære hos mit sociale netværk, som jeg endnu ikke har set?" Sådanne spørgsmål er almindelige for anbefalingssoftware, og grafdatabaser er perfekte til løsning af dem. Derudover er grafdatabaser gode til at repræsentere hierarkiske data, såsom adgangskontrol, produktkataloger, filmdatabaser eller endda netværkstopologier og organisationskort. Når du har objekter med flere forhold, finder du hurtigt ud af, at grafdatabaser tilbyder et elegant, objektorienteret paradigme til styring af disse objekter.

Sagen for grafdatabaser

Som navnet antyder, er grafdatabaser gode til at repræsentere grafer med data. Dette er især nyttigt til social software, hvor der hver gang du opretter forbindelse til nogen, defineres et forhold mellem dig. Sandsynligvis i din sidste jobsøgning valgte du et par virksomheder, som du var interesseret i, og søgte derefter på dine sociale netværk for at få forbindelse til dem. Selvom du måske ikke kender nogen, der arbejder for en af ​​disse virksomheder, gør det sandsynligvis nogen i dit sociale netværk. At løse et problem som dette er let ved en eller to adskillelsesgrader (din ven eller en ven til en ven), men hvad sker der, når du begynder at udvide søgningen på tværs af dit netværk?

I deres bog, Neo4j In Action, udforsker Aleksa Vukotic og Nicki Watt forskellene mellem relationsdatabaser og grafdatabaser til løsning af sociale netværksproblemer. Jeg vil trække på deres arbejde til de næste par eksempler for at vise dig, hvorfor grafdatabaser bliver et stadig mere populært alternativ til relationsdatabaser.

Modellering af komplekse forhold: Neo4j vs MySQL

Fra et datalogisk perspektiv, når vi tænker på modellering af relationer mellem brugere i et socialt netværk, kan vi tegne en graf som den i figur 1.

Steven Haines

En bruger har IS_FRIEND_OF forhold til andre brugere, og disse brugere har IS_FRIEND_OF forhold til andre brugere osv. Figur 2 viser, hvordan vi repræsenterer dette i en relationsdatabase.

Steven Haines

Det BRUGER tabellen har et en-til-mange forhold til USER_FRIEND tabel, der modellerer "ven" -forholdet mellem to brugere. Nu hvor vi har modelleret forholdene, hvordan ville vi spørge vores data? Vukotic og Watt målte forespørgslens ydeevne for at tælle antallet af forskellige venner, der går ud til en dybde på fem niveauer (venner af venner af venner af venner af venner). I en relationsdatabase ville forespørgslerne se således ud:

 # Dybde 1 vælg antal (forskellige uf. *) Fra user_friend uf hvor uf.user_1 =? # Dybde 2 vælg antal (forskellige uf2. *) Fra user_friend uf1 indre join user_friend uf2 på uf1.user_1 = uf2.user_2 hvor uf1.user_1 =? # Dybde 3 vælg antal (særskilt uf3. *) Fra t_user_friend uf1 indre sammenføjning t_user_friend uf2 på uf1.user_1 = uf2.user_2 indre sammenføjning t_user_friend uf3 på uf2.user_1 = uf3.user_2 hvor uf1.user_1 =? # Og så videre... 

Hvad der er interessant ved disse forespørgsler er, at hver gang vi går ud endnu et niveau, er vi forpligtet til at deltage i USER_FRIEND bord med sig selv. Tabel 1 viser, hvad forskerne Vukotic og Watt fandt, da de indsatte 1.000 brugere med ca. 50 relationer hver (50.000 relationer) og kørte forespørgslerne.

Tabel 1. MySQL-forespørgsel responstid for forskellige dybder af relationer

Dybdeudførelsestid (sekunder) Tæl resultat

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

MySQL gør et godt stykke arbejde med at slutte sig til data op til tre niveauer væk, men ydeevnen forringes hurtigt efter det. Årsagen er, at hver gang USER_FRIEND tabellen er forbundet med sig selv, skal MySQL beregne det kartesiske produkt af tabellen, selvom størstedelen af ​​dataene bliver kastet. For eksempel, når du udfører denne sammenføjning fem gange, resulterer det kartesiske produkt i 50.000 ^ 5 rækker eller 102,4 * 10 ^ 21 rækker. Det er spild, når vi kun er interesseret i 1.000 af dem!

Dernæst forsøgte Vukotic og Watt at udføre den samme type forespørgsler mod Neo4j. Disse helt forskellige resultater er vist i tabel 2.

Tabel 2. Neo4j responstid for forskellige dybder af relationer

Dybdeudførelsestid (sekunder) Tæl resultat

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

Takeaway fra disse sammenligninger er ikke at Neo4j er bedre end MySQL. I stedet for, når man krydser disse typer relationer, afhænger Neo4j's præstation af antallet af hentede poster, mens MySQL's ydeevne afhænger af antallet af poster i USER_FRIEND bord. Når antallet af forhold øges, vil responstiderne for MySQL-forespørgsler således også øges, mens svartiderne for Neo4j-forespørgsler forbliver de samme. Dette skyldes, at Neo4js responstid er afhængig af antallet af relationer for en bestemt forespørgsel og ikke af det samlede antal relationer.

Skalering af Neo4j for store data

Ved at udvide dette tankeprojekt et skridt videre skabte Vukotic og Watt derefter en million brugere med 50 millioner forhold imellem. Tabel 3 viser resultaterne for det datasæt.

Tabel 3. Neo4j-responstid for 50 millioner forhold

Dybdeudførelsestid (sekunder) Tæl resultat

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

Det er overflødigt at sige, at jeg takker Aleksa Vukotic og Nicki Watt og anbefaler stærkt at tjekke deres arbejde. Jeg hentede alle testene i dette afsnit fra det første kapitel i deres bog, Neo4j i aktion.

Kom godt i gang med Neo4j

Du har set, at Neo4j er i stand til at udføre enorme mængder af meget relaterede data meget hurtigt, og der er ingen tvivl om, at det passer bedre end MySQL (eller en hvilken som helst relationsdatabase) til visse slags problemer. Hvis du vil forstå mere om, hvordan Neo4j fungerer, er den nemmeste måde at interagere med det via webkonsollen.

Start med at downloade Neo4j. Til denne artikel vil du have Community Edition, som i skrivende stund er i version 3.2.3.

  • På en Mac skal du downloade en DMG-fil og installere den som med andre programmer.
  • På Windows skal du enten downloade en EXE og gå gennem en installationsguide eller downloade en ZIP-fil og dekomprimere den på din harddisk.
  • På Linux skal du downloade en TAR-fil og dekomprimere den på din harddisk.
  • Alternativt kan du bruge et Docker-billede på ethvert operativsystem.

Når du har installeret Neo4j, skal du starte det og åbne et browservindue til følgende URL:

//127.0.0.1:7474/browser/

Log ind med standard brugernavnet for neo4j og standardadgangskoden til neo4j. Du skal se en skærm svarende til figur 3.

Steven Haines

Knuder og relationer i Neo4j

Neo4j er designet omkring begrebet noder og relationer:

  • EN knude repræsenterer en ting, såsom en bruger, en film eller en bog.
  • En node indeholder et sæt af nøgle / værdipar, såsom et navn, en titel eller en udgiver.
  • En knude etiket definerer hvilken type ting det er - igen en bruger, en film eller en bog.
  • Relationer definere associering mellem noder og er af specifikke typer.

Som et eksempel kan vi definere tegnknudepunkter som Iron Man og Captain America; definere en filmknude med navnet "Avengers"; og derefter definere en UDSEENDE_IN forholdet mellem 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 filmknude) og to relationer (begge af typen UDSEENDE_IN).

Modellering og forespørgsel på noder og relationer

Svarende til hvordan en relationsdatabase bruger Structured Query Language (SQL) til at interagere med data, bruger Neo4j Cypher Query Language til at interagere med noder og relationer.

Lad os bruge Cypher til at skabe en simpel gengivelse af en familie. Øverst på webgrænsefladen skal du kigge efter dollartegnet. Dette indikerer et felt, der giver dig mulighed for at udføre Cypher-forespørgsler direkte mod Neo4j. Indtast følgende Cypher-forespørgsel i dette felt (jeg bruger min familie som et eksempel, men du er velkommen til at ændre detaljerne for at modellere din egen familie, hvis du vil):

OPRET (person: person {navn: "Steven", alder: 45}) TILBAGE 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 din webkonsol, vil du se dens egenskaber i bunden. I dette tilfælde er egenskaberne ID: 19, navn: Steven og alder: 45. Lad os nu nedbryde Cypher-forespørgslen:

  • SKAB: Det SKAB nøgleord bruges til at oprette noder og relationer. I dette tilfælde giver vi det et enkelt argument, som er en Person lukket i parentes, så det er meningen at oprette en enkelt node.
  • (person: Person {...}): Små bogstaver "person"er et variabelt navn, hvorigennem vi kan få adgang til den person, der oprettes, mens hovedstaden"Person"er etiketten. Bemærk, at et kolon adskiller variabelnavnet fra etiketten.
  • {navn: "Steven, alder: 45}: Dette er de nøgle- / værdiegenskaber, som vi definerer for den node, vi opretter. Neo4j kræver ikke, at du definerer et skema, før du opretter noder, og hver node kan have et unikt sæt af elementer. (Det meste af tiden definerer du noder med samme etiket til at have de samme egenskaber, men det er ikke nødvendigt.)
  • TILBAGE person: Efter at noden er oprettet, beder vi Neo4j om at returnere den tilbage til os. Dette er grunden til, at vi så noden vises i brugergrænsefladen.

Det SKAB kommando (som ikke skelnes mellem store og små bogstaver) bruges til at oprette noder og kan læses som følger: oprette en ny knude med Person-etiketten, der indeholder egenskaber for navn og alder; tildele den til personvariablen og returnere den tilbage til den, der ringer op.

Forespørgsel med Cypher Query Language

Dernæst vil vi prøve nogle forespørgsler med Cypher. Først skal vi oprette et par flere mennesker, så vi kan definere forhold mellem dem.

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

Når du har oprettet dine fire personer, kan du enten klikke på Person knappen under Node-etiketter (synlig, hvis du klikker på databaseikonet i øverste venstre hjørne af websiden) eller udfører følgende Cypher-forespørgsel:

MATCH (person: person) RETURNER person

Cypher bruger MATCH nøgleord for at finde ting i Neo4j. I dette eksempel beder vi Cypher om at matche alle noder, der har en personmærke, tildele disse noder til person variabel, og returner den værdi, der er knyttet til den variabel. Som et resultat skal du se de fire noder, du har oprettet. Hvis du holder markøren over hver node i din webkonsol, vil du se hver persons egenskaber. (Du bemærker måske, at jeg udelukkede min kones alder fra hendes knude, hvilket illustrerer, at egenskaber ikke behøver at være ensartede på tværs af knudepunkter, endda af samme etiket. Jeg er heller ikke tåbelig nok til at offentliggøre min kones alder.)

Vi kan udvide dette MATCH eksempel lidt længere ved at tilføje betingelser til de noder, vi vil returnere. For eksempel, hvis vi kun ville have "Steven" -noden, kunne vi hente den ved at matche på egenskaben name:

MATCH (person: person {name: "Steven"}) TILBAGE person

Eller hvis vi ønskede at returnere alle børnene, kunne vi bede alle mennesker under 18 år:

MATCH (person: person) HVOR person. Alder <18 RETURNER person

I dette eksempel tilføjede vi HVOR klausul til forespørgslen for at indsnævre vores resultater. HVOR fungerer meget lig dets SQL-ækvivalent: MATCH (person: person) finder alle noder med personetiketten og derefter HVOR klausul filtrerer værdier ud af resultatsættet.

Modelleringsretning i forhold

Vi har fire noder, så lad os skabe nogle relationer. Lad os først og fremmest oprette IS_MARRIED_TO forholdet mellem Steven og Linda:

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

I dette eksempel matcher vi to personnoder mærket Steven og Linda, og vi skaber et forhold af typen IS_MARRIED_TO fra Steven til Linda. Formatet til oprettelse af forholdet er som følger:

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