
利用亚马逊海王星探索图形数据库
长期以来,关系型数据库一直是所有数据持久性需求的答案(无论它们是否真的适合该解决方案)。在过去的十年里,不同类型的数据库引擎出现了爆炸性增长。这部分是由数据量和以前没有遇到过的访问模式驱动的。另一个起作用的因素是大型云计算供应商的增长。专业知识的集中化使得这些供应商能够提供专业的数据库技术,而这些技术在以前是中等规模的企业组织所不能企及的。
在这篇文章中,我将看一下这些新的数据库类型之一。图形数据库。作为其中的一部分,我们将看看Apache TinkerPop Gremlin和Amazon Neptune(AWS的图形数据库选项)。
什么是图谱数据库,我为什么要使用它?
在这里没有什么奇怪的,因为它的名字是这样的:图形数据库以一种非常容易建立关系模型的方式来存储数据;它被称为图形。我们谈论的这种图来自数学的一个分支,叫做图论;我们不是在谈论Excel和饼图。在图论中,有两个主要结构:我们有节点和边。节点代表数据实体,边代表它们之间的关系。
我为什么要这样做呢?难道SQL连接还不够好吗?嗯,不是的。对于某些用例,图形数据库胜过关系数据库。这是由于我们的数据实体之间的关系被存储在数据库本身。对于关系型数据库,你要么在运行时使用JOIN命令,要么最终有许多链接表来映射实体之间的关系。
一个典型的例子是社交网络。在下图中,我们有三个人的结点和一个乐队结点。
边缘(friends_with和likes_band)描述了实体之间的关系。以这种方式对这些实体进行映射,可以更直观地推理数据之间的关系。在关系型数据库中进行类似的查询会迅速变得非常昂贵,特别是当你扩展到数百万个节点和边的时候。
我不打算在这里列举用例,而是将其链接到Neo4j(主要的图形数据库供应商之一)提供的这个页面。它提供了一些关于这项技术的合适用例的奇妙想法。
好了,概念性的信息已经足够了,让我们来看看一些例子。我将首先在我的本地机器上运行一些例子,然后我将启动一个Amazon Neptune数据库并在那里复制同样的例子。
Apache TinkerPop Gremlin
这确实是一个东西。Gremlin是由Apache软件基金会提供的一种图形遍历语言。它是与TinkerPop兼容的图数据库一起使用的主要查询语言之一。
这里只是对Apache TinkerPop和Gremlin做一个澄清。TinkerPop可以被认为是图形的JDBC,它是一个API规范,符合TinkerPop标准的数据库必须支持该规范,以便符合TinkerPop标准。Gremlin是用于与该API交互的查询语言之一。
方便的是,Gremlin包提供了一个本地内存服务器,我们可以运行它来玩弄本地数据库。
让我们深入了解一下
我将在这里提出一个简单的例子:一个资产数据库,它对基础设施和应用服务之间的关系进行建模。下面的图片显示了这个基本模型的一个例子。在右边,我们有一些应用服务在AWS账户中运行。这些服务通过代理和VPN与一个内部的客户数据库进行通信。节点之间边缘的箭头显示了依赖关系的方向(即账户服务依赖于代理,而代理依赖于VPN等等)。
用Gephi从数据库中动态生成图形
Gremlin控制台的安装
Gremlin的安装非常简单。你只需要安装一个JVM(我试过8和11)。你可以从这里下载并解压Gremlin控制台。一旦你解压后,在你的终端中浏览到bin/目录并运行./gremlin.sh
如果一切顺利的话,你会看到下面的画面。
让我们加载一些数据
我在这个GraphML文件中为我们的简单资产数据库提供了一些数据。GraphML是一种常见的基于XML的数据格式,用于描述图结构。要理解的两个最重要的部分是我们的节点。
和我们的边
在上面我们有两个例子节点(我们的代理和账户服务)。我们把账户服务作为源,把我们的代理作为边的目标。如果你看一下上面链接的GraphML文件,你会看到所有的节点,以及描述它们之间关系的边。
为了加载我的assets.graphml文件,我把它复制到/tmp,然后从Gremlin控制台运行以下命令。
graph = TinkerGraph.open()
graph.io(graphml()).readGraph('/tmp/assets.graphml')
g = graph.traversal()
我们可以在下面的截图中看到结果。7个顶点(节点)和8条边已经被加载到数据库中。
浏览一下这些命令:
- 第1行打开一个新的TinkerGraph(TinkerPop的内存实现)的内存实例。
- 第2行读取我们的GraphML文件并将其加载到数据库中。
- 第3行实例化了一个图形遍历源对象。这将使我们能够对这个图运行遍历查询。遍历查询从一个节点走到另一个节点,与我们的标准相匹配。
我们现在能做什么?
我们已经加载了一些数据。让我们尝试一些简单的查询。
销售服务与什么有关?
很好!所以我们知道销售服务与我们的代理和支付服务有直接的依赖关系。但在查询中究竟发生了什么?Gremlin是一种函数式语言,我们可以从左到右阅读查询中的连锁函数。首先,我们得到一个名为 "销售服务 "的节点。".out() "说要找到所有与我们的节点向外连接的节点。".values('name') "返回这些节点的名称。最后,unfold()以从上到下的列表方式显示结果,而不是以数组方式一个接一个地显示。
哪些节点连接到支付服务?
这是个非常类似的查询。我们用".in() "代替".out()",所以这显示了所有连接到我们的支付服务节点的节点。
从账户服务到客户数据库有哪些路径?
有趣的是。我们有两条路径,我们的账户服务可以采取到客户数据库。要么直接,要么它可以通过 "遗留账户服务 "节点发送查询。从查询的角度来看,发生的情况如下。
- 获取名为 "账户服务 "的节点
repeat(out().simplePath()).until(has('name','Customer Database'))走过图,从 "账户服务 "开始,直到它到达 "客户数据库"。simplePath()函数意味着遍历器不会重复其在图中的路径(没有循环)。limit(10)意味着在寻找路径时,遍历器不会走得比10个节点深。在处理密集的复杂图时,这一点很重要,因为在图中遍历太深可能会影响性能。path()函数将遍历转化为路径,在本例中是通过节点的 "名称 "转化的。
更复杂的东西
对于我们的最后一个例子,让我们做一些相对复杂的事情,但我认为这显示了图数据库在正确使用情况下的力量。假设我们是一个网络管理员,我们想知道所有依赖我们的VPN的下游服务,这样我们就可以向相关的所有者传达计划中的中断。请记住,我们必须找到直接服务,以及所有直接的次级依赖,因为它们都会受到影响。
我们可以看到我们所有的依赖服务。这个查询一开始看起来很吓人,但是SQL的等价物会大得多。让我们看一下这里发生了什么:
- 我们从 "站点间VPN "节点开始,并将其设置为 "rootNode",这样我们就可以在以后的查询中引用它。
- 我们现在重复走in()节点的路径,我们递归地这样做,直到我们在该路径上没有更多的传入节点为止
where(neq('rootNode'))意味着我们的输出中不包括我们的根节点dedup()删除任何重复的节点(即我们可能两次遍历同一个节点,我们不希望在列表中出现这种情况)- 最后,我们输出找到的服务的名称
你可能已经注意到了__.in() 和__.not() 。Gremlin是基于Groovy的,下划线放在这里是为了避免与Groovy的本地关键字冲突。
现在我们看了一下Gremlin语言,让我们快速看看如何用Amazon Neptune服务做类似的事情。
亚马逊Neptune
亚马逊Neptune是一个由亚马逊提供的管理型图形数据库服务。与亚马逊Aurora RDS服务非常相似,你与数据库集群一起工作,这些集群有写入器和读取器来访问数据。更多信息可以在这里的产品页面上找到。对于我们这里的目的来说,最重要的是Neptune支持Gremlin,它还提供了对另一种叫做SPARQL的查询语言的支持,我不会在这里深究。
让我们建立一个简单的集群,看看体验是怎样的。
需要注意的是,Neptune只在VPC内可用,如果你想访问其他AWS服务(例如S3加载数据),你需要设置VPC端点以方便使用。关于这一点的更多信息在这里。
文章的这一部分有几个前提条件。1)你需要配置一个VPC,我有一个简单的VPC,配置了一个私有子网,我将在其中启动我的集群;2)你需要在同一个子网中运行一个EC2实例,以访问你的集群(通过Gremlin控制台)。
创建一个Neptune数据库
导航到Neptune控制台,点击 "创建数据库"。
注意,我只是把我的数据库引擎留成了默认提供的。现在给你的数据库集群一个唯一的标识符。
对于我们的目的,使用 "开发和测试 "模板是有意义的。
由于这只是为了演示的目的,我将使用最小的实例,即t3.medium。
在我们的演示中,不需要在多个可用区进行读复制。
现在我们必须配置我们的网络和安全设置。
注意,你选择的VPC必须是你自己的。
我选择允许控制台为我创建一个新的 "子网组"。这对这个演示并不重要。
我将让控制台创建一个名为 "shineblog-neptune "的安全组--记下这一点以便以后使用。
我保留了8182的默认端口。我们的EC2实例将在这个端口与数据库进行通信。
一旦对设置感到满意,继续点击 "创建",开始创建你的集群。
根据下面的截图,当你的数据库创建完成后,它将显示一个 "可用 "的状态。
点击集群,并注意写入者的端点名称,我们将在以后使用它来连接。
安全!
为了使我们的EC2实例能够连接到这个数据库,我们将需要修改数据库安全组。
在下面的截图中,我进入了我们的 "shineblog-neptune "安全组,该组连接到我们的数据库实例。我添加了一个自定义的TCP规则,允许从我们的EC2实例安全组(shine-neptune-testing)的默认端口8182进行通信。
从基础设施的角度来看,这应该足以让我们通过EC2实例上的Gremlin控制台进行连接。
连接到你的EC2实例
在这一点上,你会想要连接到你的EC2实例。这不在本文的讨论范围之内,但我假设你有SSH或SSM权限。
你需要在你的实例上安装和设置Gremlin控制台。我建议你去看这个文件,然后按照步骤操作。
那份文件中的几个要点我觉得很混乱或不正确:
- 在步骤6.c中,他们提到了JRE路径 "jre_path"。在我的实例上,这是/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.272.b10-1.amzn2.0.1.x86_64/jre。我通过运行以下命令找到了它
sudo /usr/sbin/alternatives *--config java* - keytool命令中提到了Gremlin控制台3.4.8,如果你按照步骤操作,应该是3.4.10。他们还提到了/home/ec2-user;如果你用会话管理器连接,那应该是/home/ssm-user。
- 第7步提到了 "your-neptune-endpoint"。这将是我提到的你应该注意到的集群端点。
如果你已经做到了这一步
我们现在应该能够从我们的实例连接到我们的Neptune集群。
[
看起来不错
在继续前行之前,需要注意的一件事是,在Neptune上运行的Gremlin实现确实有一些不同。在这里的文档中,对这些差异进行了概述。
不幸的是,我们不能通过远程连接从我们现有的GraphML文件中导入。我不得不把GraphML文件中的逻辑和数据转换成一系列我们可以从Gremlin控制台运行的语句。你可以在这里找到这个文件。在该文件中,第一个命令块插入了具有相关数据属性的节点。第二块插入了映射节点之间关系的边。你可以简单地将其复制并粘贴到你的Gremlin控制台,在EC2实例上运行。
其他导入数据的方法
有多种选择可以将数据导入到Neptune。其中一些选项是:
- 通过Gremlin控制台添加/修改/删除 - 这其实只是为了探索,在现实生活中并没有什么用处
- 使用Neptune批量加载器从S3获取数据--对批量摄取很有用。需要一个S3 VPC端点和正确的权限
- 使用AWS数据库迁移服务--对初始匹配摄取和持续复制数据有用
- 通过编程--例如通过Java Gremlin客户端
在Neptune中查询我们的图
让我们看看与上面相同的查询,但现在从我们的EC2实例远程发送至我们的实时Neptune数据库并在那里执行。

总结
唷,我们成功了在继续之前,别忘了终止你的集群。没有免费的层级,而且Neptune也不是特别便宜。
所以,总的来说,图式数据库提供了一些强大的方法来在你的数据实体之间建立连接。我真的只是触及了Gremlin可能的表面。如果你对深入了解感兴趣的话,很难超越Kelvin Lawrence的精彩的Gremlin教程。许多关于机场数据的例子真的为我带来了一些闪光点,希望它们对你也有帮助。























