亚马逊海王星的第一印象

致敬,哈布罗夫斯克居民。 期待课程的开始 “面向开发人员的 AWS” 我们准备了有趣材料的翻译。

亚马逊海王星的第一印象

在我们喜欢的许多用例中 巴克数据正如我们在客户的网站上看到的那样,相关信息隐藏在实体之间的连接中,例如在分析用户之间的关系、元素之间的依赖关系或传感器之间的连接时。 此类用例通常在图表上建模。 今年早些时候,亚马逊发布了新的图形数据库 Neptune。 在这篇文章中,我们希望分享我们的第一个想法、良好实践以及随着时间的推移可以改进的地方。

为什么我们需要 Amazon Neptune

图形数据库有望比关系数据库更好地处理高度连接的数据集。 在此类数据集中,相关信息通常存储在对象之间的关系中。 我们使用了一个令人惊叹的开放数据项目来测试海王星 MusicBrainz的。 MusicBrainz 收集各种可以想象到的音乐元数据,例如有关艺术家、歌曲、专辑发行或音乐会的信息,以及歌曲背后的艺术家与谁合作或专辑在哪个国家发行的时间。 MusicBrainz 可以被视为一个庞大的实体网络,这些实体以某种方式与音乐产业相连。

MusicBrainz 数据集以关系数据库的 CSV 转储形式提供。 转储总共包含 93 个表中的约 157 万行。 虽然其中一些表包含基本数据,例如艺术家、事件、录音、发行或曲目,但其他表 链接表 — 存储艺术家和唱片、其他艺术家或发行作品之间的关系……它们展示了数据集的图形结构。 当将数据集转换为 RDF 三元组时,我们获得了大约 500 亿个实例。

根据与我们合作的项目合作伙伴的经验和印象,我们提出了一种使用该知识库来获取新信息的设置。 此外,我们希望它能够定期更新,例如添加新版本或更新组成员。

调整

正如预期的那样,安装 Amazon Neptune 非常简单。 她说的很详细 记录在案。 只需单击几下即可启动图形数据库。 然而,当涉及到更详细的配置时, 必要的信息 很难找到。 因此,我们想要指向一个配置参数。

亚马逊海王星的第一印象
参数组的配置屏幕截图

亚马逊表示,Neptune 专注于低延迟事务工作负载,这就是默认请求超时为 120 秒的原因。 然而,我们测试了许多分析用例,在这些用例中我们经常达到这个限制。 可以通过为 Neptune 创建新参数组并设置来更改此超时 neptune_query_timeout 相应的限制。

加载数据中

下面我们将详细讨论如何将 MusicBrainz 数据加载到 Neptune 中。

三人关系

首先,我们将 MusicBrainz 数据转换为 RDF 三元组。 因此,对于每个表,我们定义了一个模板,用于定义如何在三元组中表示每一列。 在此示例中,表演者表中的每一行都映射到 XNUMX 个 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> .

批量上传

将大量数据加载到 Neptune 的建议方法是通过 S3 进行批量上传过程。 将三元组文件上传到 S3 后,您可以使用 POST 请求开始上传。 在我们的例子中,24 亿个三胞胎大约需要 500 小时。 我们预计它会更快。

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

为了避免每次启动 Neptune 时出现这个漫长的过程,我们决定从已加载这些三元组的快照中恢复实例。 从快照运行的速度明显更快,但仍需要大约一个小时才能让 Neptune 满足请求。

当最初将三元组加载到 Neptune 时,我们遇到了各种错误。

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

其中一些是解析错误,如上所示。 到目前为止,我们仍然没有弄清楚到底出了什么问题。 更详细一点肯定会有所帮助。 大约 1% 的插入三元组出现此错误。 但就测试 Neptune 而言,我们接受了这样一个事实:我们只使用来自 MusicBrainz 的 99% 的信息。

尽管这对于熟悉 SPARQL 的人来说很容易,但请注意,RDF 三元组必须使用显式数据类型进行注释,这又可能会导致错误。

流媒体下载

如上所述,我们不想将 Neptune 用作静态数据存储,而是用作灵活且不断发展的知识库。 因此,当知识库发生变化时,例如当新专辑发布或当我们想要具体化派生知识时,我们需要找到引入新三元组的方法。

Neptune 通过 SPARQL 查询支持输入运算符,包括原始查询和基于样本的查询。 我们将在下面讨论这两种方法。

我们的目标之一是以流方式输入数据。 考虑在新的国家发行专辑。 从 MusicBrainz 的角度来看,这意味着对于包含专辑、单曲、EP 等的发行版,表中会添加一个新条目 发行国家。 在 RDF 中,我们将此信息与两个新的三元组相匹配。

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

另一个目标是从图中获取新知识。 假设我们想要获取每位艺术家在其职业生涯中发行的作品数量。 这样的查询非常复杂,在 Neptune 中需要花费 20 多分钟,因此我们需要具体化结果,以便在其他查询中重用这些新知识。 因此,我们将包含此信息的三元组添加回图表中,输入子查询的结果。

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
 
}

向图中添加单个三元组需要几毫秒,而插入子查询结果的执行时间取决于子查询本身的执行时间。

虽然我们不经常使用它,但 Neptune 还允许您根据样本或显式数据删除三元组,这可用于更新信息。

SPARQL 查询

通过引入前面的子样本(返回每个艺术家的发行数量),我们已经引入了我们想要使用 Neptune 回答的第一种查询类型。 在 Neptune 中构建查询非常简单 - 向 SPARQL 端点发送 POST 请求,如下所示:

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

此外,我们还实现了一个查询,该查询返回艺术家个人资料,其中包含有关其姓名、年龄或原籍国的信息。 请记住,表演者可以是个人、乐队或管弦乐队。 此外,我们还补充了有关艺术家在这一年发行的作品数量的信息。 对于独奏艺术家,我们还添加有关艺术家每年参加的乐队的信息。

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

由于此类查询的复杂性,我们只能对特定艺术家(例如 Elton John)执行点查询,而不能对所有艺术家执行点查询。 海王星似乎没有通过将过滤器放入子选择中来优化此类查询。 因此,每个选择都必须按艺术家姓名手动过滤。

Neptune 按小时收费和按 I/O 收费。 在我们的测试中,我们使用了最低限度的 Neptune 实例,其成本为 0,384 美元/小时。 在上面的查询中,计算单个工作人员的配置文件,Amazon 向我们收取数万次 I/O 操作的费用,这意味着成本为 0.02 美元。

结论

首先,Amazon Neptune 兑现了大部分承诺。 作为一项托管服务,它是一个非常易于安装的图形数据库,无需进行大量配置即可启动和运行。 以下是我们的五个主要发现:

  • 批量上传很简单,但速度很慢。 但它可能会因为错误消息而变得复杂,而这些错误消息并不是很有帮助。
  • 流式下载支持我们期望的一切并且速度相当快
  • 查询很简单,但交互性不足以运行分析查询
  • SPARQL 查询必须手动优化
  • 亚马逊付款很难估计,因为很难估计 SPARQL 查询扫描的数据量。

就这样。 注册 关于“负载平衡”主题的免费网络研讨会.


来源: habr.com

添加评论