Primeras impresiones de Amazon Neptune

Saludo, Khabrovsk. En previsión del inicio del curso de AWS para desarrolladores, preparamos una traducción de material interesante.




En muchos casos de usuarios que, como bakdata , vemos en los sitios de nuestros clientes, la información relevante se oculta en las relaciones entre entidades, por ejemplo, al analizar relaciones entre usuarios, dependencias entre elementos o conexiones entre sensores. Tales casos de usuario generalmente se modelan en un gráfico. A principios de este año, Amazon lanzó la nueva base de datos de gráficos Neptune. En esta publicación, queremos compartir nuestras primeras ideas, buenas prácticas y lo que se puede mejorar con el tiempo.

¿Por qué necesitamos Amazon Neptune?


Las bases de datos de gráficos prometen manejar conjuntos de datos altamente conectados mejor que sus equivalentes relacionales. En tales conjuntos de datos, la información relevante generalmente se almacena en las relaciones entre los objetos. Para probar Neptune utilizamos un proyecto increíble con datos abiertos MusicBrainz . MusicBrainz recopila cualquier metadato concebible sobre música, como información sobre artistas, canciones, lanzamientos de álbumes o conciertos, así como con quién colaboró ​​el artista que creó la canción o cuándo se lanzó el álbum en qué país. MusicBrainz puede verse como una gran red de entidades que de alguna manera están relacionadas con la industria de la música.

El conjunto de datos MusicBrainz se proporciona como un volcado de la base de datos relacional CSV. En total, el volcado contiene alrededor de 93 millones de filas en 157 tablas. Si bien algunas de estas tablas contienen datos maestros, como artistas, eventos, registros, lanzamientos o pistas, otras - tablas de enlaces - almacenan relaciones entre artistas y registros, otros artistas o lanzamientos, etc. Muestran la estructura gráfica del conjunto. datos. Al convertir el conjunto de datos en triples RDF, obtuvimos alrededor de 500 millones de copias.

Con base en la experiencia e impresiones de los socios del proyecto con quienes trabajamos, presentamos un entorno en el que esta base de conocimiento se utiliza para obtener nueva información. Además, suponemos que se actualizará periódicamente, por ejemplo, agregando nuevas versiones o actualizando miembros del grupo.

Personalización


Como se esperaba, instalar Amazon Neptune es fácil. Está documentado con cierto detalle . Puede iniciar la base de datos de gráficos con solo unos pocos clics. Sin embargo, cuando se trata de una configuración más detallada, la información que necesita es difícil de encontrar. Por lo tanto, queremos apuntar a un parámetro de configuración.


La captura de pantalla de configuración para los grupos de parámetros de

Amazon indica que Neptune se enfoca en cargas de trabajo transaccionales de baja latencia, por lo que el tiempo de espera predeterminado es de 120 segundos. Sin embargo, probamos muchos casos de usuarios analíticos en los que regularmente alcanzamos este límite. Puede cambiar este tiempo de espera creando un nuevo grupo de parámetros para Neptuno y configurándolo enneptune_query_timeout Restricción correspondiente.

Carga de datos


A continuación discutiremos en detalle cómo cargamos los datos de MusicBrainz a Neptune.

Relaciones en los tres


Primero, convertimos los datos de MusicBrainz a triples RDF. Por lo tanto, para cada tabla, definimos una plantilla que determina cómo se representa cada columna en los tres primeros. En este ejemplo, cada fila de la tabla de ejecutor se asigna a doce triples 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> .


Subida masiva


El método propuesto para cargar grandes cantidades de datos en Neptune es el proceso de carga masiva a través de S3. Después de cargar sus archivos triples a S3, comienza la descarga utilizando una solicitud POST. En nuestro caso, tomó alrededor de 24 horas por 500 millones de triples. Esperábamos que fuera más rápido.

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

Para evitar este largo proceso cada vez que lanzamos Neptune, decidimos restaurar la instancia a partir de la instantánea en la que estos triples ya están cargados. Comenzar desde una instantánea es mucho más rápido, pero todavía toma aproximadamente una hora hasta que Neptune esté disponible para las solicitudes.

Cuando inicialmente cargamos triples en Neptuno, encontramos varios errores.

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

Algunos de ellos fueron errores de análisis, como se muestra arriba. Hasta la fecha, todavía no hemos descubierto qué salió mal exactamente en este momento. Un poco más de detalle definitivamente ayudaría aquí. Este error ocurrió para aproximadamente el 1% de los triples insertados. Pero en cuanto a probar Neptuno, aceptamos el hecho de que solo trabajamos con el 99% de la información de MusicBrainz.

Incluso si es fácil para las personas familiarizadas con SPARQL, tenga en cuenta que los triples RDF deben ser anotados con tipos de datos explícitos, lo que nuevamente puede causar errores.

Descarga en streaming


Como se mencionó anteriormente, no queremos usar Neptune como un almacén de datos estático, sino más bien como una base de conocimiento flexible y en evolución. Por lo tanto, necesitábamos encontrar formas de introducir nuevos triples al cambiar la base de conocimiento, por ejemplo, cuando se publica un nuevo álbum o cuando queremos materializar el conocimiento derivado.

Neptune admite operadores de entrada a través de consultas SPARQL, tanto con datos sin procesar como basados ​​en muestras. Discutiremos ambos enfoques a continuación.

Uno de nuestros objetivos era ingresar datos en modo de transmisión. Considere el lanzamiento de un álbum en un nuevo país. Desde el punto de vista de MusicBrainz, esto significa que para un lanzamiento que incluye álbumes, singles, EPs, etc., se agrega un nuevo registro a la tabla del país del lanzamiento. En RDF, comparamos esta información con dos nuevos triples.

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

Otro objetivo era obtener nuevos conocimientos del gráfico. Supongamos que queremos obtener el número de lanzamientos que cada artista ha publicado en su carrera. Dicha consulta es bastante complicada y lleva más de 20 minutos en Neptuno, por lo que debemos materializar el resultado para reutilizar este nuevo conocimiento en alguna otra consulta. Por lo tanto, agregamos triples con esta información nuevamente al gráfico, introduciendo el resultado de la subconsulta.

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
 
}

Agregar triples individuales a un gráfico lleva varios milisegundos, mientras que el tiempo de ejecución para insertar el resultado de una subconsulta depende del tiempo de ejecución de la propia subconsulta.

A pesar de que no lo usamos a menudo, Neptune también le permite eliminar triples basados ​​en muestras o datos explícitos que se pueden utilizar para actualizar la información.

Consultas SPARQL


Al presentar la submuestra anterior, que devuelve el número de lanzamientos para cada artista, ya hemos introducido el primer tipo de solicitud que queremos responder usando Neptune. Crear una consulta en Neptuno es fácil: envíe una solicitud POST al punto final SPARQL, como se muestra a continuación:

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

Además, implementamos una consulta que devuelve un perfil de artista que contiene información sobre su nombre, edad o país de origen. Tenga en cuenta que los artistas pueden ser personas, grupos u orquestas. Además, complementamos estos datos con información sobre el número de lanzamientos lanzados por artistas durante el año. Para los artistas solistas, también agregamos información sobre los grupos en los que estos artistas participaron cada año.

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

Debido a la complejidad de dicha solicitud, solo pudimos realizar consultas de puntos para un artista específico, como Elton John, pero no para todos los artistas. Neptuno no parece optimizar dicha consulta al omitir los filtros en las submuestras. Por lo tanto, cada muestra debe filtrarse manualmente por el nombre del artista.

Neptuno tiene tanto por hora como por pago para cada operación de E / S. Para nuestras pruebas, utilizamos la instancia de Neptuno más pequeña, que cuesta $ 0.384 / hora. En el caso de la solicitud anterior, que calcula el perfil de un artista, Amazon nos cobrará decenas de miles de E / S, lo que implica un costo de $ 0.02.

Conclusión


En primer lugar, Amazon Neptune cumple la mayoría de sus promesas. Al ser un servicio administrado, es una base de datos gráfica que es extremadamente fácil de instalar y se puede iniciar sin mucha configuración. Aquí están nuestros cinco hallazgos clave:

  • La carga masiva es simple pero lenta. Pero puede complicarse debido a los mensajes de error que no ayudan mucho
  • La carga de flujo soportó todo lo que esperábamos, y fue lo suficientemente rápido
  • Las consultas son simples pero no lo suficientemente interactivas como para realizar consultas analíticas.
  • Las consultas SPARQL deben optimizarse manualmente
  • Los pagos de Amazon son difíciles de evaluar porque es difícil estimar la cantidad de datos escaneados por una consulta SPARQL.

Eso es todo. Regístrese para un seminario web gratuito sobre el tema "Equilibrio de carga" .

All Articles