Første indtryk af Amazon Neptun

Salut, Khabrovsk beboere. I forventning om kursusstart "AWS for udviklere" Vi har udarbejdet en oversættelse af interessant materiale.

Første indtryk af Amazon Neptun

I mange tilfælde, som vi godt kan lide bakdataSom vi ser på vores kunders hjemmesider, er relevant information skjult i forbindelser mellem enheder, for eksempel ved analyse af relationer mellem brugere, afhængigheder mellem elementer eller forbindelser mellem sensorer. Sådanne use cases er normalt modelleret på en graf. Tidligere i år udgav Amazon sin nye grafdatabase, Neptune. I dette indlæg vil vi dele vores første ideer, god praksis og hvad der kan forbedres over tid.

Hvorfor vi havde brug for Amazon Neptun

Grafdatabaser lover at håndtere stærkt forbundne datasæt bedre end deres relationelle ækvivalenter. I sådanne datasæt lagres relevant information normalt i relationer mellem objekter. Vi brugte et fantastisk åbent dataprojekt til at teste Neptune MusicBrainz. MusicBrainz indsamler alle tænkelige musikmetadata, såsom information om kunstnere, sange, albumudgivelser eller koncerter, samt hvem kunstneren bag sangen samarbejdede med, eller hvornår albummet blev udgivet i hvilket land. MusicBrainz kan ses som et enormt netværk af enheder, der på en eller anden måde er forbundet med musikindustrien.

MusicBrainz-datasættet leveres som et CSV-dump af en relationel database. I alt indeholder lossepladsen omkring 93 millioner rækker i 157 tabeller. Mens nogle af disse tabeller indeholder grundlæggende data såsom kunstnere, begivenheder, optagelser, udgivelser eller spor, andre link tabeller — lagerforhold mellem kunstnere og optagelser, andre kunstnere eller udgivelser osv... De demonstrerer grafstrukturen af ​​et datasæt. Da vi konverterede datasættet til RDF-tripler, opnåede vi cirka 500 millioner forekomster.

På baggrund af erfaringer og indtryk fra de projektpartnere, som vi samarbejder med, præsenterer vi en ramme, hvor denne vidensbase bruges til at indhente ny information. Derudover forventer vi, at den bliver opdateret løbende, for eksempel ved at tilføje nye udgivelser eller opdatere gruppemedlemmer.

justering

Som forventet er det nemt at installere Amazon Neptune. Hun er ret detaljeret dokumenteret. Du kan starte en grafdatabase med blot et par klik. Men når det kommer til mere detaljeret konfiguration, nødvendige oplysninger svært at finde. Derfor vil vi pege på én konfigurationsparameter.

Første indtryk af Amazon Neptun
Konfigurationsskærmbillede for parametergrupper

Amazon siger, at Neptune fokuserer på transaktionsarbejdsbelastninger med lav latens, hvorfor standardanmodningstimeout er 120 sekunder. Vi har dog testet mange analytiske use cases, hvor vi jævnligt nåede denne grænse. Denne timeout kan ændres ved at oprette en ny parametergruppe for Neptun og indstilling neptune_query_timeout tilsvarende begrænsning.

Indlæser data

Nedenfor vil vi diskutere i detaljer, hvordan vi indlæste MusicBrainz-data i Neptun.

Relationer i tre

Først konverterede vi MusicBrainz-dataene til RDF-tripler. Derfor har vi for hver tabel defineret en skabelon, der definerer, hvordan hver kolonne er repræsenteret i triplen. I dette eksempel er hver række fra performer-tabellen afbildet til tolv RDF-tripler.

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

Masseupload

Den foreslåede måde at indlæse store mængder data i Neptune er gennem masseoverførselsprocessen via S3. Når du har uploadet dine tredobbelte filer til S3, begynder du uploaden ved hjælp af en POST-anmodning. I vores tilfælde tog det omkring 24 timer for 500 millioner trillinger. Vi forventede, at det var hurtigere.

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 at undgå denne langvarige proces, hver gang vi starter Neptune, besluttede vi at gendanne forekomsten fra et øjebliksbillede, hvor disse tripletter allerede var indlæst. At køre fra et øjebliksbillede er betydeligt hurtigere, men det tager stadig omkring en time, før Neptune er tilgængelig for anmodninger.

Da vi oprindeligt indlæste tripletter i Neptun, stødte vi på forskellige fejl.

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

Nogle af dem var parsingsfejl, som vist ovenfor. Til dato har vi stadig ikke fundet ud af, hvad der præcist gik galt på nuværende tidspunkt. Lidt flere detaljer ville helt sikkert hjælpe her. Denne fejl opstod for ca. 1 % af de indsatte tripler. Men hvad angår test af Neptune, accepterede vi det faktum, at vi kun arbejder med 99% af informationerne fra MusicBrainz.

Selvom dette er nemt for folk, der er bekendt med SPARQL, skal du være opmærksom på, at RDF-tripler skal annoteres med eksplicitte datatyper, hvilket igen kan forårsage fejl.

Streaming download

Som nævnt ovenfor ønsker vi ikke at bruge Neptun som et statisk datalager, men snarere som en fleksibel og udviklende videnbase. Så vi var nødt til at finde måder at introducere nye tripler på, når vidensbasen ændres, for eksempel når et nyt album udkommer, eller når vi vil materialisere afledt viden.

Neptune understøtter input-operatører gennem SPARQL-forespørgsler, både rå- og prøvebaserede. Vi vil diskutere begge tilgange nedenfor.

Et af vores mål var at indtaste data på en streaming måde. Overvej at udgive et album i et nyt land. Fra MusicBrainz' perspektiv betyder det, at for en udgivelse, der inkluderer albums, singler, EP'er osv., tilføjes en ny post til tabellen udgivelsesland. I RDF matcher vi denne information med to nye tripler.

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 andet mål var at få ny viden fra grafen. Lad os sige, at vi ønsker at få det antal udgivelser, hver kunstner har udgivet i deres karriere. Sådan en forespørgsel er ret kompleks og tager over 20 minutter i Neptun, så vi er nødt til at materialisere resultatet for at genbruge denne nye viden i en anden forespørgsel. Så vi tilføjer tripler med denne information tilbage til grafen og indtaster resultatet af underforespørgslen.

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
 
}

Tilføjelse af enkelte tripler til grafen tager et par millisekunder, mens udførelsestiden for at indsætte resultatet af en underforespørgsel afhænger af udførelsestiden for selve underforespørgslen.

Selvom vi ikke brugte det ofte, giver Neptune dig også mulighed for at fjerne tripletter baseret på prøver eller eksplicitte data, som kan bruges til at opdatere information.

SPARQL-forespørgsler

Ved at introducere den forrige delprøve, som returnerer antallet af udgivelser for hver kunstner, har vi allerede introduceret den første type forespørgsel, vi ønsker at besvare ved hjælp af Neptune. Det er nemt at bygge en forespørgsel i Neptun - send en POST-anmodning til SPARQL-slutpunktet, 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

Derudover har vi implementeret en forespørgsel, der returnerer en kunstnerprofil, der indeholder oplysninger om deres navn, alder eller oprindelsesland. Husk, at de optrædende kan være enkeltpersoner, bands eller orkestre. Derudover supplerer vi disse data med oplysninger om antallet af udgivelser udgivet af kunstnere i løbet af året. For soloartister tilføjer vi også oplysninger om de bands, som kunstneren deltog 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å grund af kompleksiteten af ​​en sådan forespørgsel kunne vi kun udføre punktforespørgsler for en bestemt kunstner, såsom Elton John, men ikke for alle kunstnere. Neptun ser ikke ud til at optimere en sådan forespørgsel ved at droppe filtre i undervalg. Derfor skal hvert valg manuelt filtreres efter kunstnernavn.

Neptun har både time- og per-I/O opladninger. Til vores test brugte vi det absolutte minimum Neptune-forekomst, som koster $0,384/time. I tilfælde af forespørgslen ovenfor, som beregner profilen for en enkelt arbejder, opkræver Amazon os titusindvis af I/O-operationer, hvilket indebærer en omkostning på $0.02.

Output

For det første holder Amazon Neptune de fleste af sine løfter. Som en administreret service er det en grafdatabase, der er ekstremt nem at installere og kan være oppe og køre uden en masse konfiguration. Her er vores fem nøgleresultater:

  • Masseupload er nemt, men langsomt. Men det kan blive kompliceret med fejlmeddelelser, der ikke er særlig nyttige.
  • Streaming download understøtter alt, hvad vi forventede og var ret hurtig
  • Forespørgsler er enkle, men ikke interaktive nok til at køre analytiske forespørgsler
  • SPARQL-forespørgsler skal optimeres manuelt
  • Amazon-betalinger er svære at estimere, fordi det er svært at estimere mængden af ​​data, der scannes af en SPARQL-forespørgsel.

Det er alt. Tilmeld dig gratis webinar om emnet "Load Balancing".


Kilde: www.habr.com

Tilføj en kommentar