Førsteinntrykk av Amazon Neptun

Hilsen, innbyggere i Khabrovsk. I påvente av kursstart "AWS for utviklere" Vi har utarbeidet en oversettelse av interessant materiale.

Førsteinntrykk av Amazon Neptun

I mange brukssaker som vi liker bakdataSom vi ser på våre kunders nettsider, er relevant informasjon skjult i forbindelser mellom enheter, for eksempel ved analyse av relasjoner mellom brukere, avhengigheter mellom elementer, eller forbindelser mellom sensorer. Slike brukstilfeller er vanligvis modellert på en graf. Tidligere i år ga Amazon ut sin nye grafdatabase, Neptune. I dette innlegget ønsker vi å dele våre første ideer, gode fremgangsmåter og hva som kan forbedres over tid.

Hvorfor vi trengte Amazon Neptun

Grafdatabaser lover å håndtere svært tilkoblede datasett bedre enn deres relasjonsekvivalenter. I slike datasett er relevant informasjon vanligvis lagret i relasjoner mellom objekter. Vi brukte et fantastisk åpent dataprosjekt for å teste Neptune MusicBrainz. MusicBrainz samler inn alle slags musikkmetadata som kan tenkes, for eksempel informasjon om artister, sanger, albumutgivelser eller konserter, samt hvem artisten bak sangen samarbeidet med eller når albumet ble utgitt i hvilket land. MusicBrainz kan sees på som et stort nettverk av enheter som på en eller annen måte er knyttet til musikkindustrien.

MusicBrainz-datasettet leveres som en CSV-dump av en relasjonsdatabase. Totalt inneholder dumpen omtrent 93 millioner rader fordelt på 157 tabeller. Mens noen av disse tabellene inneholder grunnleggende data som artister, arrangementer, opptak, utgivelser eller spor, andre lenke tabeller — lagre forhold mellom artister og innspillinger, andre artister eller utgivelser, osv... De demonstrerer grafstrukturen til et datasett. Når vi konverterte datasettet til RDF-trippel, fikk vi omtrent 500 millioner forekomster.

Basert på erfaring og inntrykk fra prosjektpartnerne vi samarbeider med, presenterer vi en setting der denne kunnskapsbasen brukes til å innhente ny informasjon. I tillegg forventer vi at den oppdateres jevnlig, for eksempel ved å legge til nye utgivelser eller oppdatere gruppemedlemmer.

justering

Som forventet er det enkelt å installere Amazon Neptune. Hun er ganske detaljert dokumentert. Du kan starte en grafdatabase med bare noen få klikk. Men når det gjelder mer detaljert konfigurasjon, nødvendig informasjon vanskelig å finne. Derfor ønsker vi å peke på én konfigurasjonsparameter.

Førsteinntrykk av Amazon Neptun
Skjermbilde for konfigurasjon for parametergrupper

Amazon sier at Neptune fokuserer på transaksjonelle arbeidsbelastninger med lav latens, og det er grunnen til at standard forespørselstidsavbrudd er 120 sekunder. Vi har imidlertid testet mange analytiske brukstilfeller der vi regelmessig nådde denne grensen. Denne tidsavbruddet kan endres ved å opprette en ny parametergruppe for Neptun og innstilling neptune_query_timeout tilsvarende begrensning.

Laster inn data

Nedenfor vil vi diskutere i detalj hvordan vi lastet MusicBrainz-data inn i Neptune.

Relasjoner i treere

Først konverterte vi MusicBrainz-dataene til RDF-trippel. Derfor, for hver tabell, definerte vi en mal som definerer hvordan hver kolonne er representert i trippelen. I dette eksemplet er hver rad fra utøvertabellen tilordnet til tolv RDF-trippel.

<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/gid> "${gid}"^^<http://www.w3.org/2001/XMLSchema#string> .
 
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/name> "${name}"^^<http://www.w3.org/2001/XMLSchema#string> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/sort-name> "${sort_name}"^^<http://www.w3.org/2001/XMLSchema#string> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/begin-date> "${begin_date_year}-${begin_date_month}-${begin_date_day}"^^xsd:<http://www.w3.org/2001/XMLSchema#date> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/end-date> "${end_date_year}-${end_date_month}-${end_date_day}"^^xsd:<http://www.w3.org/2001/XMLSchema#date> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/type> <http://musicbrainz.foo/artist-type/${type}> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/area> <http://musicbrainz.foo/area/${area}> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/gender> <http://musicbrainz.foo/gender/${gender}> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/comment> "${comment}"^^<http://www.w3.org/2001/XMLSchema#string> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/edits-pending> "${edits_pending}"^^<http://www.w3.org/2001/XMLSchema#int> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/last-updated> "${last_updated}"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
 
<http://musicbrainz.foo/artist/${id}> <http://musicbrainz.foo/ended> "${ended}"^^<http://www.w3.org/2001/XMLSchema#boolean> .

Masseopplasting

Den foreslåtte måten å laste inn store mengder data i Neptune er gjennom masseopplastingsprosessen via S3. Etter å ha lastet opp de tredoble filene dine til S3, begynner du opplastingen ved å bruke en POST-forespørsel. I vårt tilfelle tok det omtrent 24 timer for 500 millioner trillinger. Vi forventet at det skulle gå raskere.

curl -X POST -H 'Content-Type: application/json' http://your-neptune-cluster:8182/loader -d '{
 
 
 "source" : "s3://your-s3-bucket",
 
 "format" : "ntriples",
 
 "iamRoleArn" : "arn:aws:iam::your-iam-user:role/NeptuneLoadFromS3",
 
 "region" : "eu-west-1",
 
 "failOnError" : "FALSE"
 
}'

For å unngå denne lange prosessen hver gang vi starter Neptune, bestemte vi oss for å gjenopprette forekomsten fra et øyeblikksbilde der disse trillingene allerede var lastet inn. Å kjøre fra et øyeblikksbilde er betydelig raskere, men det tar fortsatt omtrent en time før Neptune er tilgjengelig for forespørsler.

Da vi først lastet trillinger inn i Neptun, oppdaget vi forskjellige feil.

{
 
 
 "errorCode" : "PARSING_ERROR",
 
 "errorMessage" : "Content after '.' is not allowed",
 
 "fileName" : [...],
 
 "recordNum" : 25
 
}

Noen av dem var parsefeil, som vist ovenfor. Til dags dato har vi fortsatt ikke funnet ut hva som gikk galt på dette tidspunktet. Litt mer detaljer vil definitivt hjelpe her. Denne feilen oppsto for omtrent 1 % av de innsatte trippel. Men når det gjelder testing av Neptune, aksepterte vi det faktum at vi bare jobber med 99 % av informasjonen fra MusicBrainz.

Selv om dette er enkelt for folk som er kjent med SPARQL, vær oppmerksom på at RDF-trippel må merkes med eksplisitte datatyper, som igjen kan forårsake feil.

Streaming nedlasting

Som nevnt ovenfor ønsker vi ikke å bruke Neptun som et statisk datalager, men snarere som en fleksibel og utviklende kunnskapsbase. Så vi trengte å finne måter å introdusere nye trippel når kunnskapsbasen endres, for eksempel når et nytt album publiseres eller når vi ønsker å materialisere avledet kunnskap.

Neptune støtter input-operatører gjennom SPARQL-spørringer, både rå- og prøvebaserte. Vi vil diskutere begge tilnærmingene nedenfor.

Et av målene våre var å legge inn data på en streaming måte. Vurder å gi ut et album i et nytt land. Fra MusicBrainz sitt perspektiv betyr dette at for en utgivelse som inkluderer album, singler, EP-er osv., legges det til en ny oppføring på tabellen utgivelsesland. I RDF matcher vi denne informasjonen med to nye trippel.

INSERT DATA { <http://musicbrainz.foo/release-country/737041> <http://musicbrainz.foo/release> <http://musicbrainz.foo/release/435759> };INSERT DATA { <http://musicbrainz.foo/release-country/737041> <http://musicbrainz.foo/date-year> "2018"^^<http://www.w3.org/2001/XMLSchema#int> };

Et annet mål var å få ny kunnskap fra grafen. La oss si at vi ønsker å få antall utgivelser hver artist har publisert i sin karriere. En slik spørring er ganske kompleks og tar over 20 minutter i Neptun, så vi må materialisere resultatet for å gjenbruke denne nye kunnskapen i en annen spørring. Så vi legger til trippel med denne informasjonen tilbake til grafen, og legger inn resultatet av underspørringen.

INSERT {
 
 
  ?artist_credit <http://musicbrainz.foo/number-of-releases> ?number_of_releases
 
} WHERE {
 
  SELECT ?artist_credit (COUNT(*) as ?number_of_releases)
 
  WHERE {
 
     ?artist_credit <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/artist-credit> .
 
     ?release_group <http://musicbrainz.foo/artist-credit> ?artist_credit .
 
     ?release_group <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/release-group> .
 
     ?release_group <http://musicbrainz.foo/name> ?release_group_name .
 
  }
 
  GROUP BY ?artist_credit
 
}

Å legge til enkle trippel til grafen tar noen få millisekunder, mens utførelsestiden for å sette inn resultatet av en delspørring avhenger av utførelsestiden til selve delspørringen.

Selv om vi ikke brukte det ofte, lar Neptune deg også fjerne trillinger basert på prøver eller eksplisitte data, som kan brukes til å oppdatere informasjon.

SPARQL-spørringer

Ved å introdusere den forrige delprøven, som returnerer antall utgivelser for hver artist, har vi allerede introdusert den første typen spørringer vi ønsker å besvare ved å bruke Neptune. Det er enkelt å bygge en spørring i Neptun – send en POST-forespørsel til SPARQL-endepunktet, som vist nedenfor:

curl -X POST --data-binary 'query=SELECT ?artist ?p ?o where {?artist <http://musicbrainz.foo/name> "Elton John" . ?artist ?p ?o . }' http://your-neptune-cluster:8182/sparql

I tillegg har vi implementert et søk som returnerer en artistprofil som inneholder informasjon om deres navn, alder eller opprinnelsesland. Husk at utøverne kan være enkeltpersoner, band eller orkestre. I tillegg supplerer vi disse dataene med informasjon om antall utgivelser gitt ut av artister i løpet av året. For soloartister legger vi også til informasjon om bandene som artisten deltok i hvert år.

SELECT
 
 
 ?artist_name ?year
 
 ?releases_in_year ?releases_up_year
 
 ?artist_type_name ?releases
 
 ?artist_gender ?artist_country_name
 
 ?artist_begin_date ?bands
 
 ?bands_in_year
 
WHERE {
 
 # Bands for each artist
 
 {
 
   SELECT
 
     ?year
 
     ?first_artist
 
     (group_concat(DISTINCT ?second_artist_name;separator=",") as ?bands)
 
     (COUNT(DISTINCT ?second_artist_name) AS ?bands_in_year)     
 
   WHERE {
 
     VALUES ?year {
 
       1960 1961 1962 1963 1964 1965 1966 1967 1968 1969
 
       1970 1971 1972 1973 1974 1975 1976 1977 1978 1979
 
       1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
 
       1990 1991 1992 1993 1994 1995 1996 1997 1998 1999
 
       2000 2001 2002 2003 2004 2005 2006 2007 2008 2009
 
       2010 2011 2012 2013 2014 2015 2016 2017 2018
 
     }   
 
     ?first_artist <http://musicbrainz.foo/name> "Elton John" .
 
     ?first_artist <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/artist> .
 
     ?first_artist <http://musicbrainz.foo/type> ?first_artist_type .
 
     ?first_artist <http://musicbrainz.foo/name> ?first_artist_name .
 

 
 
     ?second_artist <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/artist> .
 
     ?second_artist <http://musicbrainz.foo/type> ?second_artist_type .
 
     ?second_artist <http://musicbrainz.foo/name> ?second_artist_name .
 
     optional { ?second_artist <http://musicbrainz.foo/begin-date-year> ?second_artist_begin_date_year . }
 
     optional { ?second_artist <http://musicbrainz.foo/end-date-year> ?second_artist_end_date_year . }
 

 
 
     ?l_artist_artist <http://musicbrainz.foo/entity0> ?first_artist .
 
     ?l_artist_artist <http://musicbrainz.foo/entity1> ?second_artist .
 
     ?l_artist_artist <http://musicbrainz.foo/link> ?link .
 

 
 
     optional { ?link <http://musicbrainz.foo/begin-date-year> ?link_begin_date_year . }
 
     optional { ?link <http://musicbrainz.foo/end-date-year> ?link_end_date_year . }
 

 
 
     FILTER (!bound(?link_begin_date_year) || ?link_begin_date_year <= ?year)
 
     FILTER (!bound(?link_end_date_year) || ?link_end_date_year >= ?year)
 
     FILTER (!bound(?second_artist_begin_date_year) || ?second_artist_begin_date_year <= ?year)
 
     FILTER (!bound(?second_artist_end_date_year) || ?second_artist_end_date_year >= ?year)
 
     FILTER (?first_artist_type NOT IN (<http://musicbrainz.foo/artist-type/2>, <http://musicbrainz.foo/artist-type/5>, <http://musicbrainz.foo/artist-type/6>))
 
     FILTER (?second_artist_type IN (<http://musicbrainz.foo/artist-type/2>, <http://musicbrainz.foo/artist-type/5>, <http://musicbrainz.foo/artist-type/6>))
 
   }
 
   GROUP BY ?first_artist ?year
 
 }
 
 # Releases up to a year
 
 {
 
   SELECT
 
     ?artist
 
     ?year
 
     (group_concat(DISTINCT ?release_name;separator=",") as ?releases)
 
     (COUNT(*) as ?releases_up_year)
 
   WHERE {
 
     VALUES ?year {
 
       1960 1961 1962 1963 1964 1965 1966 1967 1968 1969
 
       1970 1971 1972 1973 1974 1975 1976 1977 1978 1979
 
       1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
 
       1990 1991 1992 1993 1994 1995 1996 1997 1998 1999
 
       2000 2001 2002 2003 2004 2005 2006 2007 2008 2009
 
       2010 2011 2012 2013 2014 2015 2016 2017 2018 
 
     }
 

 
 
     ?artist <http://musicbrainz.foo/name> "Elton John" .
 

 
 
     ?artist_credit_name <http://musicbrainz.foo/artist-credit> ?artist_credit .
 
     ?artist_credit_name <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/artist-credit-name> .
 
     ?artist_credit_name <http://musicbrainz.foo/artist> ?artist .
 
     ?artist_credit <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/artist-credit> .
 

 
 
     ?release_group <http://musicbrainz.foo/artist-credit> ?artist_credit .
 
     ?release_group <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/release-group> .
 
     ?release_group <http://musicbrainz.foo/name> ?release_group_name .
 
     ?release <http://musicbrainz.foo/release-group> ?release_group .
 
     ?release <http://musicbrainz.foo/name> ?release_name .
 
     ?release_country <http://musicbrainz.foo/release> ?release .
 
     ?release_country <http://musicbrainz.foo/date-year> ?release_country_year .
 

 
 
     FILTER (?release_country_year <= ?year)
 
   }
 
   GROUP BY ?artist ?year
 
 }
 
 # Releases in a year
 
 {
 
   SELECT ?artist ?year (COUNT(*) as ?releases_in_year)
 
   WHERE {
 
     VALUES ?year {
 
       1960 1961 1962 1963 1964 1965 1966 1967 1968 1969
 
       1970 1971 1972 1973 1974 1975 1976 1977 1978 1979
 
       1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
 
       1990 1991 1992 1993 1994 1995 1996 1997 1998 1999
 
       2000 2001 2002 2003 2004 2005 2006 2007 2008 2009
 
       2010 2011 2012 2013 2014 2015 2016 2017 2018 
 
     }
 

 
 
     ?artist <http://musicbrainz.foo/name> "Elton John" .
 

 
 
     ?artist_credit_name <http://musicbrainz.foo/artist-credit> ?artist_credit .
 
     ?artist_credit_name <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/artist-credit-name> .
 
     ?artist_credit_name <http://musicbrainz.foo/artist> ?artist .
 
     ?artist_credit <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/artist-credit> .
 

 
 
     ?release_group <http://musicbrainz.foo/artist-credit> ?artist_credit .
 
     ?release_group <http://musicbrainz.foo/rdftype> <http://musicbrainz.foo/release-group> .
 
     ?release_group <http://musicbrainz.foo/name> ?release_group_name .
 
     ?release <http://musicbrainz.foo/release-group> ?release_group .
 
     ?release_country <http://musicbrainz.foo/release> ?release .
 
     ?release_country <http://musicbrainz.foo/date-year> ?release_country_year .
 

 
 
     FILTER (?release_country_year = ?year)
 
   }
 
   GROUP BY ?artist ?year
 
 }
 
 # Master data
 
 {
 
   SELECT DISTINCT ?artist ?artist_name ?artist_gender ?artist_begin_date ?artist_country_name
 
   WHERE {
 
     ?artist <http://musicbrainz.foo/name> ?artist_name .
 
     ?artist <http://musicbrainz.foo/name> "Elton John" .
 
     ?artist <http://musicbrainz.foo/gender> ?artist_gender_id .
 
     ?artist_gender_id <http://musicbrainz.foo/name> ?artist_gender .
 
     ?artist <http://musicbrainz.foo/area> ?birth_area .
 
     ?artist <http://musicbrainz.foo/begin-date-year> ?artist_begin_date.
 
     ?birth_area <http://musicbrainz.foo/name> ?artist_country_name .
 

 
 
     FILTER(datatype(?artist_begin_date) = xsd:int)
 
   }

På grunn av kompleksiteten til et slikt søk, kunne vi bare utføre punktsøk for en bestemt artist, for eksempel Elton John, men ikke for alle artister. Neptun ser ikke ut til å optimalisere en slik spørring ved å slippe filtre i undervalg. Derfor må hvert utvalg filtreres manuelt etter artistnavn.

Neptun har både time- og per-I/O-ladninger. For vår testing brukte vi minimum Neptune-forekomsten, som koster $0,384/time. I tilfellet med spørringen ovenfor, som beregner profilen for en enkelt arbeider, belaster Amazon oss titusenvis av I/O-operasjoner, noe som innebærer en kostnad på $0.02.

Utgang

For det første holder Amazon Neptune de fleste løftene sine. Som en administrert tjeneste er det en grafdatabase som er ekstremt enkel å installere og kan være oppe og kjøre uten mye konfigurasjon. Her er våre fem hovedfunn:

  • Masseopplasting er enkelt, men tregt. Men det kan bli komplisert med feilmeldinger som ikke er veldig nyttige.
  • Streaming-nedlasting støtter alt vi forventet og var ganske rask
  • Spørringer er enkle, men ikke interaktive nok til å kjøre analytiske spørringer
  • SPARQL-spørringer må optimaliseres manuelt
  • Amazon-betalinger er vanskelig å estimere fordi det er vanskelig å estimere mengden data skannet av en SPARQL-spørring.

Det er alt. Melde seg på gratis webinar om emnet "Load Balancing".


Kilde: www.habr.com

Legg til en kommentar