Primeiras impressões do Amazon Netuno

Saudação, Khabrovsk. Antecipando o início do curso da AWS para desenvolvedores, preparamos uma tradução de material interessante.




Em muitos casos de usuários que, como bakdata , vemos nos sites de nossos clientes, informações relevantes ficam ocultas nos relacionamentos entre entidades, por exemplo, ao analisar relacionamentos entre usuários, dependências entre elementos ou conexões entre sensores. Esses casos de usuário geralmente são modelados em um gráfico. No início deste ano, a Amazon lançou o novo banco de dados de gráficos Netuno. Neste post, queremos compartilhar nossas primeiras idéias, boas práticas e o que pode ser aprimorado ao longo do tempo.

Por que precisamos do Amazon Neptune


Os bancos de dados de gráficos prometem lidar com conjuntos de dados altamente conectados melhor do que seus equivalentes relacionais. Nesses conjuntos de dados, as informações relevantes geralmente são armazenadas nos relacionamentos entre objetos. Para testar o Neptune, usamos um projeto incrível com o MusicBrainz de dados abertos . O MusicBrainz coleta quaisquer metadados concebíveis sobre a música, como informações sobre artistas, músicas, lançamentos ou concertos de álbuns, bem como com quem o artista que criou a música colaborou ou quando o álbum foi lançado em que país. O MusicBrainz pode ser visto como uma enorme rede de entidades que de alguma forma estão relacionadas à indústria da música.

O conjunto de dados MusicBrainz é fornecido como um despejo do banco de dados relacional CSV. No total, o dump contém cerca de 93 milhões de linhas em 157 tabelas. Enquanto algumas dessas tabelas contêm dados mestre, como artistas, eventos, registros, lançamentos ou faixas, outras - tabelas de links - armazenam relacionamentos entre artistas e registros, outros artistas ou lançamentos, etc ... Eles demonstram a estrutura gráfica do conjunto dados. Ao converter o conjunto de dados em RDF triplos, obtivemos cerca de 500 milhões de cópias.

Com base na experiência e nas impressões dos parceiros do projeto com quem trabalhamos, apresentamos um cenário em que essa base de conhecimento é usada para obter novas informações. Além disso, assumimos que ele será atualizado regularmente, por exemplo, adicionando novos lançamentos ou atualizando membros do grupo.

Costumização


Como esperado, a instalação do Amazon Neptune é fácil. Está documentado em alguns detalhes . Você pode iniciar o banco de dados gráfico em apenas alguns cliques. No entanto, quando se trata de uma configuração mais detalhada, é difícil encontrar as informações necessárias . Portanto, queremos apontar para um parâmetro de configuração.


A captura de tela de configuração dos grupos de parâmetros da

Amazon indica que o Neptune se concentra nas cargas de trabalho transacionais de baixa latência; portanto, o tempo limite padrão é de 120 segundos. No entanto, testamos muitos casos de usuários analíticos nos quais atingimos esse limite regularmente. Você pode alterar esse tempo limite criando um novo grupo de parâmetros para o Netuno e definindo comoneptune_query_timeout restrição correspondente.

Carregamento de dados


A seguir, discutiremos detalhadamente como carregamos os dados do MusicBrainz no Neptune.

Relações nas três


Primeiro, convertemos os dados do MusicBrainz em triplos RDF. Portanto, para cada tabela, definimos um modelo que determina como cada coluna é representada nos três primeiros. Neste exemplo, cada linha da tabela do executor é mapeada para doze triplos 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> .


Upload em massa


O método proposto para carregar grandes quantidades de dados no Neptune é o processo de carregamento em massa através do S3. Depois de enviar seus arquivos triplos para o S3, inicie o download usando uma solicitação POST. No nosso caso, foram necessárias 24 horas para 500 milhões de triplos. Esperávamos que fosse mais 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 esse processo demorado toda vez que lançamos o Neptune, decidimos restaurar a instância do instantâneo no qual esses triplos já estão carregados. A partir de um instantâneo é muito mais rápido, mas ainda leva cerca de uma hora até que o Netuno esteja disponível para solicitações.

Ao carregar inicialmente triplos no Netuno, encontramos vários erros.

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

Alguns deles estavam analisando erros, como mostrado acima. Até o momento, ainda não descobrimos o que exatamente deu errado no momento. Um pouco mais de detalhes definitivamente ajudaria aqui. Este erro ocorreu em cerca de 1% dos triplos inseridos. Porém, quanto ao teste de Netuno, aceitamos o fato de trabalharmos apenas com 99% das informações do MusicBrainz.

Mesmo que seja fácil para as pessoas familiarizadas com o SPARQL, lembre-se de que os triplos do RDF devem ser anotados com tipos de dados explícitos, o que novamente pode causar erros.

Download de streaming


Como mencionado acima, não queremos usar o Netuno como um data warehouse estático, mas como uma base de conhecimento flexível e em evolução. Portanto, precisávamos encontrar maneiras de introduzir novos triplos ao alterar a base de conhecimento, por exemplo, quando um novo álbum é publicado ou quando queremos materializar o conhecimento derivado.

O Netuno suporta operadores de entrada por meio de consultas SPARQL, com dados brutos e com base em amostras. Discutiremos as duas abordagens abaixo.

Um dos nossos objetivos era inserir dados no modo de streaming. Considere o lançamento de um álbum em um novo país. Do ponto de vista do MusicBrainz, isso significa que, para um lançamento que inclui álbuns, singles, EPs etc., um novo registro é adicionado à tabela de países do lançamento. No RDF, comparamos essas informações com dois novos triplos.

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

Outro objetivo era obter novos conhecimentos a partir do gráfico. Suponha que queremos obter o número de lançamentos que cada artista publicou em sua carreira. Essa consulta é bastante complicada e leva mais de 20 minutos no Netuno, portanto, precisamos materializar o resultado para reutilizar esse novo conhecimento em outra consulta. Portanto, adicionamos triplos com essas informações de volta ao gráfico, apresentando o resultado da 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
 
}

A adição de triplos únicos a um gráfico leva vários milissegundos, enquanto o tempo de execução para inserir o resultado de uma subconsulta depende do tempo de execução da própria subconsulta.

Apesar de não usá-lo com frequência, o Netuno também permite excluir triplos com base em amostras ou dados explícitos que podem ser usados ​​para atualizar as informações.

Consultas SPARQL


Apresentando a subamostra anterior, que retorna o número de lançamentos para cada artista, já introduzimos o primeiro tipo de solicitação que queremos responder usando o Neptune. Criar uma consulta no Neptune é fácil - envie uma solicitação POST para o terminal SPARQL, conforme mostrado abaixo:

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

Além disso, implementamos uma consulta que retorna um perfil de artista contendo informações sobre seu nome, idade ou país de origem. Lembre-se de que artistas podem ser pessoas, grupos ou orquestras. Além disso, complementamos esses dados com informações sobre o número de lançamentos lançados por artistas durante o ano. Para artistas solo, também adicionamos informações sobre os grupos em que esses artistas participaram todos os anos.

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

Devido à complexidade de tal solicitação, só foi possível realizar consultas de pontos para um artista específico, como Elton John, mas não para todos os artistas. Netuno parece não otimizar essa consulta, omitindo filtros em subamostras. Portanto, cada amostra deve ser filtrada manualmente pelo nome do artista.

Netuno tem uma taxa horária e paga por cada operação de E / S. Para nossos testes, usamos a menor instância do Netuno, que custa US $ 0,384 / hora. No caso da solicitação acima, que calcula o perfil de um artista, a Amazon cobrará dezenas de milhares de E / S, o que implica um custo de US $ 0,02.

Conclusão


Primeiro, o Amazon Neptune mantém a maioria de suas promessas. Por ser um serviço gerenciado, é um banco de dados gráfico extremamente fácil de instalar e pode ser iniciado sem muitas configurações. Aqui estão nossas cinco principais conclusões:

  • O upload em massa é simples, mas lento. Mas pode ficar complicado devido a mensagens de erro que não ajudam muito
  • O carregamento do fluxo suportava tudo o que esperávamos e era rápido o suficiente
  • As consultas são simples, mas não interativas o suficiente para realizar consultas analíticas
  • As consultas SPARQL devem ser otimizadas manualmente
  • Os pagamentos da Amazon são difíceis de avaliar porque é difícil estimar a quantidade de dados verificados por uma consulta SPARQL.

Isso é tudo. Inscreva-se em um seminário on-line gratuito sobre o tópico "Balanceamento de carga" .

All Articles