Amazon Neptune: premières impressions

Salut, Khabrovsk. En prévision du début du cours AWS for Developers, nous avons préparé une traduction de matériel intéressant.




Dans de nombreux cas d'utilisateurs que nous, en tant que bakdata , voyons sur les sites de nos clients, les informations pertinentes sont cachées dans les relations entre les entités, par exemple, lors de l'analyse des relations entre les utilisateurs, des dépendances entre les éléments ou des connexions entre les capteurs. Ces cas d'utilisation sont généralement modélisés sur un graphique. Plus tôt cette année, Amazon a publié la nouvelle base de données de graphes Neptune. Dans cet article, nous voulons partager nos premières idées, bonnes pratiques et ce qui peut être amélioré au fil du temps.

Pourquoi avons-nous besoin d'Amazon Neptune


Les bases de données graphiques promettent de mieux gérer les ensembles de données hautement connectés que leurs équivalents relationnels. Dans ces ensembles de données, les informations pertinentes sont généralement stockées dans les relations entre les objets. Pour tester Neptune, nous avons utilisé un projet étonnant avec les données ouvertes MusicBrainz . MusicBrainz recueille toutes les métadonnées imaginables sur la musique, telles que des informations sur les artistes, les chansons, les sorties d'albums ou les concerts, ainsi que les personnes avec lesquelles l'artiste qui a créé la chanson a collaboré ou la date de sortie de l'album dans quel pays. MusicBrainz peut être considéré comme un immense réseau d'entités qui sont en quelque sorte liées à l'industrie de la musique.

L'ensemble de données MusicBrainz est fourni en tant que vidage de la base de données relationnelle CSV. Au total, le vidage contient environ 93 millions de lignes dans 157 tables. Alors que certaines de ces tables contiennent des données de base, telles que des artistes, des événements, des enregistrements, des sorties ou des pistes, d'autres - des tables de liens - stockent les relations entre les artistes et les enregistrements, d'autres artistes ou sorties, etc ... Elles montrent la structure graphique de l'ensemble Les données. Lors de la conversion de l'ensemble de données en triplets RDF, nous avons obtenu environ 500 millions d'exemplaires.

Sur la base de l'expérience et des impressions des partenaires du projet avec lesquels nous travaillons, nous présentons un cadre dans lequel cette base de connaissances est utilisée pour obtenir de nouvelles informations. De plus, nous supposons qu'il sera régulièrement mis à jour, par exemple, en ajoutant de nouvelles versions ou en mettant à jour les membres du groupe.

Personnalisation


Comme prévu, l'installation d'Amazon Neptune est facile. Il est documenté en détail . Vous pouvez démarrer la base de données graphique en quelques clics. Cependant, lorsqu'il s'agit d'une configuration plus détaillée, les informations dont vous avez besoin sont difficiles à trouver. Par conséquent, nous voulons pointer vers un paramètre de configuration.


La capture d'écran de configuration des groupes de paramètres

Amazon affirme que Neptune se concentre sur les charges de travail transactionnelles à faible latence, le délai d'expiration par défaut est donc de 120 secondes. Cependant, nous avons testé de nombreux cas d'utilisateurs analytiques dans lesquels nous avons régulièrement atteint cette limite. Vous pouvez modifier ce délai d'expiration en créant un nouveau groupe de paramètres pour Neptune et en définissant surneptune_query_timeout restriction correspondante.

Chargement des données


Ci-dessous, nous discuterons en détail de la façon dont nous avons téléchargé les données MusicBrainz sur Neptune.

Des relations à trois


Tout d'abord, nous avons converti les données MusicBrainz en triplets RDF. Par conséquent, pour chaque table, nous avons défini un modèle qui détermine comment chaque colonne est représentée dans les trois premiers. Dans cet exemple, chaque ligne de la table de l'exécuteur est mappée à douze triplets RDF.

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


Transfert groupé


La méthode proposée pour charger de grandes quantités de données dans Neptune est le processus de chargement en masse via S3. Après avoir téléchargé vos triplets sur S3, vous démarrez le téléchargement à l'aide d'une demande POST. Dans notre cas, il a fallu environ 24 heures pour 500 millions de triplets. Nous nous attendions à ce que ce soit plus rapide.

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"
 
}'

Pour éviter ce long processus à chaque lancement de Neptune, nous avons décidé de restaurer l'instance à partir de l'instantané dans lequel ces triplets sont déjà chargés. Le démarrage à partir d'un instantané est beaucoup plus rapide, mais il faut toujours environ une heure pour que Neptune soit disponible pour les demandes.

Lors du chargement initial de triplets dans Neptune, nous avons rencontré diverses erreurs.

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

Certains d'entre eux étaient des erreurs d'analyse, comme indiqué ci-dessus. À ce jour, nous n'avons toujours pas compris ce qui s'est exactement passé en ce moment. Un peu plus de détails aiderait certainement ici. Cette erreur s'est produite pour environ 1% des triplets insérés. Mais en ce qui concerne les tests de Neptune, nous avons accepté le fait que nous ne travaillons qu'avec 99% des informations de MusicBrainz.

Même si c'est facile pour les personnes familiarisées avec SPARQL, gardez à l'esprit que les triplets RDF doivent être annotés avec des types de données explicites, ce qui peut à nouveau provoquer des erreurs.

Téléchargement en streaming


Comme mentionné ci-dessus, nous ne voulons pas utiliser Neptune comme un entrepôt de données statique, mais plutôt comme une base de connaissances flexible et évolutive. Par conséquent, nous devions trouver des moyens d'introduire de nouveaux triplets lors du changement de la base de connaissances, par exemple, lorsqu'un nouvel album est publié ou lorsque nous voulons matérialiser des connaissances dérivées.

Neptune prend en charge les opérateurs d'entrée via des requêtes SPARQL, à la fois avec des données brutes et basées sur des échantillons. Nous discuterons des deux approches ci-dessous.

L'un de nos objectifs était de saisir des données en mode streaming. Considérez la sortie d'un album dans un nouveau pays. Du point de vue de MusicBrainz, cela signifie que pour une sortie qui inclut des albums, des singles, des EP, etc., un nouvel enregistrement est ajouté à la table des pays de sortie. Dans RDF, nous comparons ces informations avec deux nouveaux triplets.

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> };

Un autre objectif était d'obtenir de nouvelles connaissances à partir du graphique. Supposons que nous voulons obtenir le nombre de sorties que chaque artiste a publiées dans sa carrière. Une telle requête est plutôt compliquée et prend plus de 20 minutes à Neptune, nous devons donc matérialiser le résultat afin de réutiliser ces nouvelles connaissances dans une autre requête. Par conséquent, nous ajoutons des triplets avec ces informations au graphique, introduisant le résultat de la sous-requête.

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
 
}

L'ajout de triplets simples à un graphique prend plusieurs millisecondes, tandis que le temps d'exécution pour l'insertion du résultat d'une sous-requête dépend du temps d'exécution de la sous-requête elle-même.

Malgré le fait que nous ne l'utilisions pas souvent, Neptune vous permet également de supprimer des triplets basés sur des échantillons ou des données explicites qui peuvent être utilisés pour mettre à jour les informations.

Requêtes SPARQL


En présentant le sous-échantillon précédent, qui renvoie le nombre de sorties pour chaque artiste, nous avons déjà introduit le premier type de demande auquel nous voulons répondre en utilisant Neptune. La création d'une requête dans Neptune est facile - envoyez une demande POST au point de terminaison SPARQL, comme indiqué ci-dessous:

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

De plus, nous avons implémenté une requête qui renvoie un profil d'artiste contenant des informations sur son nom, son âge ou son pays d'origine. Gardez à l'esprit que les interprètes peuvent être des personnes, des groupes ou des orchestres. De plus, nous complétons ces données par des informations sur le nombre de sorties publiées par les artistes au cours de l'année. Pour les artistes solos, nous ajoutons également des informations sur les groupes auxquels ces artistes ont participé chaque année.

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)
 
   }

En raison de la complexité d'une telle demande, nous ne pouvions effectuer des requêtes ponctuelles que pour un artiste spécifique, comme Elton John, mais pas pour tous les artistes. Neptune ne semble pas optimiser une telle requête en omettant les filtres dans les sous-échantillons. Par conséquent, chaque échantillon doit être filtré manuellement par le nom de l'artiste.

Neptune a à la fois horaire et paye pour chaque opération d'E / S. Pour nos tests, nous avons utilisé la plus petite instance de Neptune, qui coûte 0,384 $ / heure. Dans le cas de la demande ci-dessus, qui calcule le profil d'un artiste, Amazon nous facturera des dizaines de milliers d'E / S, ce qui implique un coût de 0,02 $.

Conclusion


Tout d'abord, Amazon Neptune tient la plupart de ses promesses. Étant un service géré, il s'agit d'une base de données graphique extrêmement facile à installer et pouvant être lancée sans beaucoup de paramètres. Voici nos cinq principales conclusions:

  • Le téléchargement en masse est simple mais lent. Mais cela peut devenir compliqué en raison de messages d'erreur qui ne sont pas très utiles
  • Le chargement de flux a pris en charge tout ce que nous attendions et a été assez rapide
  • Les requêtes sont simples mais pas suffisamment interactives pour effectuer des requêtes analytiques
  • Les requêtes SPARQL doivent être optimisées manuellement
  • Les paiements Amazon sont difficiles à évaluer car il est difficile d'estimer la quantité de données analysées par une requête SPARQL.

C'est tout. Inscrivez-vous à un webinaire gratuit sur le thème «Équilibrage de charge» .

All Articles