Premières impressions d'Amazon Neptune

Salut, habitants de Khabrovsk. En prévision du début des cours "AWS pour les développeurs" Nous avons préparé une traduction de matériel intéressant.

Premières impressions d'Amazon Neptune

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

Pourquoi nous avions 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 de tels ensembles de données, les informations pertinentes sont généralement stockées dans les relations entre les objets. Nous avons utilisé un incroyable projet de données ouvertes pour tester Neptune MusicBrainz. MusicBrainz collecte tous les types de métadonnées musicales imaginables, telles que des informations sur les artistes, les chansons, les sorties d'albums ou les concerts, ainsi que la personne avec laquelle l'artiste à l'origine de la chanson a collaboré ou la date de sortie de l'album dans quel pays. MusicBrainz peut être considéré comme un vaste réseau d’entités connectées d’une manière ou d’une autre à l’industrie musicale.

L'ensemble de données MusicBrainz est fourni sous forme de dump CSV d'une base de données relationnelle. Au total, le dump contient environ 93 millions de lignes réparties dans 157 tables. Alors que certains de ces tableaux contiennent des données de base telles que les artistes, les événements, les enregistrements, les sorties ou les pistes, d'autres lier les tables — stocker les relations entre les artistes et les enregistrements, d'autres artistes ou sorties, etc... Ils démontrent la structure graphique d'un ensemble de données. Lors de la conversion de l'ensemble de données en triples RDF, nous avons obtenu environ 500 millions d'instances.

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 nous attendons à ce qu'il soit mis à jour régulièrement, par exemple en ajoutant de nouvelles versions ou en mettant à jour les membres du groupe.

réglage

Comme prévu, l'installation d'Amazon Neptune est simple. Elle est assez détaillée documenté. Vous pouvez lancer une base de données de graphiques en quelques clics. Cependant, lorsqu'il s'agit d'une configuration plus détaillée, informations nécessaires difficile à trouver. Par conséquent, nous souhaitons souligner un paramètre de configuration.

Premières impressions d'Amazon Neptune
Capture d'écran de configuration pour les groupes de paramètres

Amazon affirme que Neptune se concentre sur les charges de travail transactionnelles à faible latence, c'est pourquoi le délai d'expiration de la demande par défaut est de 120 secondes. Nous avons cependant testé de nombreux cas d’usage analytiques dans lesquels nous atteignions régulièrement cette limite. Ce délai d'attente peut être modifié en créant un nouveau groupe de paramètres pour Neptune et en définissant neptune_query_timeout restriction correspondante.

Chargement des données

Ci-dessous, nous discuterons en détail de la manière dont nous avons chargé les données MusicBrainz dans Neptune.

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éfinit la manière dont chaque colonne est représentée dans le triplet. Dans cet exemple, chaque ligne de la table des interprètes est mappée sur 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 manière suggérée de charger de grandes quantités de données dans Neptune consiste à utiliser le processus de téléchargement groupé via S3. Après avoir téléchargé vos fichiers triples sur S3, vous commencez le téléchargement à l'aide d'une requête POST. Dans notre cas, il a fallu environ 24 heures pour 500 millions de triplés. 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 d'un instantané dans lequel ces triplets étaient déjà chargés. L'exécution à partir d'un instantané est nettement plus rapide, mais prend encore environ une heure jusqu'à ce que Neptune soit disponible pour les requêtes.

Lors du chargement initial des triplés dans Neptune, nous avons rencontré diverses erreurs.

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

Certaines d’entre elles étaient des erreurs d’analyse, comme indiqué ci-dessus. À ce jour, nous n’avons toujours pas compris exactement ce qui n’allait pas à ce stade. Un peu plus de détails aiderait certainement ici. Cette erreur s'est produite pour environ 1 % des triples 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 provenant de MusicBrainz.

Même si cela est facile pour les personnes familiarisées avec SPARQL, sachez que les triplets RDF doivent être annotés avec des types de données explicites, ce qui peut encore une fois provoquer des erreurs.

Téléchargement en continu

Comme mentionné ci-dessus, nous ne souhaitons pas utiliser Neptune comme un magasin de données statique, mais plutôt comme une base de connaissances flexible et évolutive. Il fallait donc trouver des moyens d'introduire de nouveaux triplets lorsque la base de connaissances change, par exemple lorsqu'un nouvel album est publié ou lorsque l'on souhaite matérialiser des connaissances dérivées.

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

L'un de nos objectifs était de saisir les données de manière continue. Pensez à sortir un album dans un nouveau pays. Du point de vue de MusicBrainz, cela signifie que pour une sortie comprenant des albums, des singles, des EP, etc., une nouvelle entrée est ajoutée au tableau. pays de sortie. En RDF, nous associons cette information à 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'acquérir de nouvelles connaissances à partir du graphique. Disons que nous voulons obtenir le nombre de sorties que chaque artiste a publiées au cours de sa carrière. Une telle requête est assez complexe et prend plus de 20 minutes dans Neptune, il faut donc matérialiser le résultat afin de réutiliser ces nouvelles connaissances dans une autre requête. Nous ajoutons donc des triplets avec ces informations au graphique, en entrant 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 triples simples au graphique prend quelques millisecondes, tandis que le temps d'exécution pour insérer le résultat d'une sous-requête dépend du temps d'exécution de la sous-requête elle-même.

Bien que nous ne l'ayons pas souvent utilisé, 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 des informations.

Requêtes SPARQL

En introduisant le sous-échantillon précédent, qui renvoie le nombre de sorties pour chaque artiste, nous avons déjà introduit le premier type de requête auquel nous souhaitons répondre à l'aide de Neptune. Créer une requête dans Neptune est simple : envoyez une requête 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 individus, des groupes ou des orchestres. De plus, nous complétons ces données par des informations sur le nombre de sorties sorties par les artistes au cours de l'année. Pour les artistes solos, nous ajoutons également des informations sur les groupes auxquels l'artiste a 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 requête, 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 supprimant les filtres dans les sous-sélections. Par conséquent, chaque sélection doit être filtrée manuellement par nom d’artiste.

Neptune facture des frais horaires et par E/S. Pour nos tests, nous avons utilisé le strict minimum d'instance Neptune, qui coûte 0,384 $/heure. Dans le cas de la requête ci-dessus, qui calcule le profil d'un seul travailleur, Amazon nous facture des dizaines de milliers d'opérations d'E/S, ce qui implique un coût de 0.02 $.

conclusion

Premièrement, Amazon Neptune tient la plupart de ses promesses. En tant que service géré, il s'agit d'une base de données graphique extrêmement facile à installer et qui peut être opérationnelle sans beaucoup de configuration. Voici nos cinq principales conclusions :

  • Le téléchargement groupé est facile mais lent. Mais cela peut se compliquer avec des messages d’erreur qui ne sont pas très utiles.
  • Le téléchargement en streaming prend en charge tout ce que nous attendions et a été assez rapide
  • Les requêtes sont simples, mais pas suffisamment interactives pour exécuter des requêtes analytiques
  • Les requêtes SPARQL doivent être optimisées manuellement
  • Les paiements Amazon sont difficiles à estimer car il est difficile d'estimer la quantité de données analysées par une requête SPARQL.

C'est tout. Inscrivez vous pour webinaire gratuit sur le thème « Load Balancing ».


Source: habr.com

Ajouter un commentaire