Salut, Khabrovsk beboere. I forventning om kursusstart
I mange tilfælde, som vi godt kan lide
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-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
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
Kilde: www.habr.com