前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >十的次方 - 第一部分

十的次方 - 第一部分

作者头像
大数据弄潮儿
发布2018-06-01 17:17:33
1.7K0
发布2018-06-01 17:17:33
举报
文章被收录于专栏:大数据大数据

这篇文章最初由Stephen Mallette和Daniel Kuppitz在Aurelius发表。

“不,不!先开始冒险吧,”狮鹫不耐烦地说道,“解释起来需要太多的时间。” - 刘易斯卡罗尔 - 爱丽丝梦游仙境

设想使用Titan的好处往往很简单。对拥有数十亿条边的分布图进行复杂的图分析像是有待进行的冒险。就像刘易斯卡罗尔的故事中的狮鹫一样,我们对立刻进行这场冒险有着强烈的欲望。很明显但又有些遗憾的是,Titan的优势直到其中存有一些数据时才能显现。考虑下面的解释 ; 他们是将数据大量加载到Titan的策略,使冒险成为可能。

各种不同的变量可能会影响将数据加载到图中的方法,但为决策提供最重要指导的属性是大小。就本文而言,“大小”是指要加载到图中的估计数。用于加载数据的策略倾向于以10的幂次改变,其中用于加载100万条边的策略与用于1000万条边的不同。

鉴于批量加载策略分类的整洁和令人难忘的方式,这篇由两部分组成的文章概述了每个策略从100万或更少的最小值开始,并继续保持10到10亿或更多的权限。第一部分将重点介绍100万和1000万条边缘,涉及一些Gremlin的常见操作。第二部分将重点关注1亿和10亿个边缘,将涉及到Faunus的基本使用。

100万

在数百万以及更少的边的范围内,确实没有特别的加载策略可以遵循,因为图可以完全载入内存,加载时间也相当快。这种规模的即便发生错误,其解决成本也不会太高,因为问题通常很容易诊断,并且我们可以重新加载图,而不必等待太多时间。

正如之前发表的一篇名为Polyglot Persistence and Query with Gremlin的博客文章所解释的,Gremlin REPL是一个处理任何类型数据的灵活环境。很明显,它提供了像Titan这样的图形数据库的访问,但是在同一个REPL会话中,也可以连接到关系数据库,接触到Web服务,读取文件等。有了这个功能,编写Gremlin脚本可以通过REPL执行的操作可能是将数据导入图的最轻量级和直接的方式。

维基选票网站(包含了维基百科从2008年1月成立之初至今所有的维基百科投票数据,网络中的顶点代表了维基的用户,其中由箭头线连接的顶点i至j代表了用户i给用户j的投票)。在其基本制表符分隔的数据结构中,包含7,115个顶点和103,689条边,这是我们演示的合适的大小。

在开始我们的示例之前,我们需要下载并解压最新版本的Titan(titan-all包)。接着,我们在Titan的根目录下载并解压维基百科选票网站数据集:

代码语言:txt
复制
$ curl -L -O http://snap.stanford.edu/data/wiki-Vote.txt.gz
$ gunzip wiki-Vote.txt.gz

解压后将在Titan根目录下得到wiki-Vote.txt文件。下面的Gremlin脚本演示了如何将该文件加载到Titan中(由BerkleyDB支持):

代码语言:txt
复制
g = TitanFactory.open('/tmp/1m')
g.makeKey('userId').dataType(String.class).indexed(Vertex.class).unique().make()
g.makeLabel('votesFor').make()
g.commit()
getOrCreate = { id ->
defp = g.V('userId', id)
if(p.hasNext()) ? p.next() : g.addVertex([userId:id])
}
newFile('wiki-Vote.txt').eachLine{
if(!it.startsWith("#")){
(fromVertex, toVertex) = it.split('\t').collect(getOrCreate)
fromVertex.addEdge('votesFor', toVertex)
}
}
g.commit()

数据加载脚本要注意的关键部分如下:

  • g.makeKey(‘userId’)…- 首先在Titan中创建类型。在这种情况下,表中将只包含存在于每个用户顶点的userId。始终在类型创建结束时以及在将数据加载到图形实例之前进行提交。
  • getOrCreate = { id ->... - 将顶点标识符(即userId)作为参数并执行索引查找以确定顶点是否已存在的辅助函数。如果存在,则返回顶点,但如果它不存在,则会创建该顶点。getOrCreate是一个常见的概念,并且它是执行此类任务时所必需的、高效的函数。
  • new File('wiki-Vote.txt').eachLine {- 逐行读取源数据文件,并对每个文件执行提供的闭包
  • if (!it.startsWith("#")){ - 该文件包含由#标识开头的注释行。这些行应该被忽略。
  • (fromVertex, toVertex) = it.split('\t').collect(getOrCreate) - 每行由一对制表符分隔的值组成。此代码将选项卡上的文本行分割以创建包含两个userID值的列表。collect函数将处理getOrCreate所得到的值,然后将所得列表解构到的两个顶点变量存入已经存在或以其它方式被新创建的图中:fromVertextoVertex
  • fromVertex.addEdge('votesFor', toVertex) - 构造两个顶点之间的边。
  • g.commit()- 值得注意的是,这个加载是在单个事务的上下文中执行的。在处理100万条边或更多时,我们有必要在过程中执行中间提交。

要执行此脚本,请将其复制到Titan安装目录根目录下的文件中。请注意,该脚本将在文件系统上生成Titan数据库。开始Gremlin 。当REPL初始化后执行脚本如下:load-1m.groovy/tmp/1mbin/gremlin.sh

代码语言:txt
复制
$ bin/gremlin.sh
\,,,/
(o o)
-----oOOo-(_)-oOOo-----
gremlin> \. load-1m.groovy
==>titangraph[local:/tmp/1m]
==>userId
...
==>null
gremlin> g.V.count()
==>7115
gremlin> g.E.count()
==>103689

维基选票网站的图表模式比较简单。即使是100万条边的规模,复杂性也仅仅来自批量加载脚本。本节中的加载脚本提供了一个良好的框架,我们可以在其上实现更加复杂的加载。

1000万

加载数千万条边的方法与上一节没有太大区别。Gremlin脚本仍然是最直接的加载方法,但是需要考虑一些差异。这些差异中最重要的是BatchGraph的使用,它在指定的时间间隔处理事务的中间提交,并维护顶点缓存以便快速检索。有关其使用限制的重要信息,请参阅BatchGraph文档

DocGraph数据集“展示了医疗保健提供者团队如何提供护理”。该网络中的顶点代表医疗服务提供者,它们由NPI number标识。边表示两个提供者之间的共享交互,其中三个属性进一步限定了该交互。数据根据时间窗口分成几种尺寸。本节将利用“30天信息窗口”,其中包含大约100万个顶点和7300万条边。

从Titan的根目录下载并解包DocGraph数据集:

代码语言:txt
复制
$ curl -L -O http://bit.ly/DocGraph2012-2013-Days30zip
$ unzip DocGraph2012-2013-Days30zip && rm DocGraph2012-2013-Days30zip
$ head -n3 DocGraph-2012-2013-Days30.csv
$ sort DocGraph-2012-2013-Days30.csv > DocGraph-2012-2013-Days30-sorted.csv

解压缩存档将在Titan目录的根目录下创建DocGraph-2012-2013-Days30.csv。与上一节中的情况不同,数据是按每条边外顶点的NPI number预先分类的。对数据进行预先排序有助于提高BatchGraph的性能,因为缓存的写入和刷新次数会减少。下面的Gremlin脚本演示了如何将该文件加载到Titan中(由BerkleyDB支持):

代码语言:txt
复制
conf = newBaseConfiguration() {{
setProperty("storage.backend", "berkeleyje")
setProperty("storage.directory", "/tmp/10m")
setProperty("storage.batch-loading", true)
}}
g = TitanFactory.open(conf)
g.makeKey("npi").dataType(String.class).single().unique().indexed(Vertex.class).make()
sharedTransactionCount = g.makeKey("sharedTxCount").dataType(Integer.class).make()
patientTotal = g.makeKey("patientTotal").dataType(Integer.class).make()
sameDayTotal = g.makeKey("sameDayTotal").dataType(Integer.class).make()
g.makeLabel("shares").signature(sharedTransactionCount, patientTotal, sameDayTotal).make()
g.commit()
bg = newBatchGraph(g, VertexIDType.STRING, 10000)
bg.setVertexIdKey("npi")
c = 0L
newFile("DocGraph-2012-2013-Days30-sorted.csv").eachLine({ final String line ->
def(id1,
id2,
sharedTransactionCount,
patientTotal,
sameDayTotal) = line.split(',')*.trim()
defv1 = bg.getVertex(id1) ?: bg.addVertex(id1)
defv2 = bg.getVertex(id2) ?: bg.addVertex(id2)
defedge = bg.addEdge(null, v1, v2, "shares")
edge.setProperty("sharedTxCount", sharedTransactionCount asInteger)
edge.setProperty("patientTotal", patientTotal asInteger)
edge.setProperty("sameDayTotal", sameDayTotal asInteger)
if(++c%100000L == 0L) println"Processed ${c} edges"
})
bg.commit()

下面是对这个脚本的解析(它可以在Gremlin REPL中按照前一节提供的说明执行):

  • setProperty("storage.batch-loading", true) - 为Titan启用“批量加载”将通过禁用一致性检查和锁定来帮助提高性能。阅读更多关于此选项以及可能影响批量加载的其他设置,请参阅Titan 文档
  • g.makeKey("npi")... - 正如前面的100万边缘规模的例子,首先应该创建并提交类型。
  • bg = new BatchGraph(g, VertexIDType.STRING, 10000)- 将TitanGraph实例包装入BatchGraph,定义标识符的数据类型,在这种情况下,对于NPI number是STRING ,并将事务大小设置为10000。使用此设置,BatchGraph将自动将每10,000次突变事务提交到图表。
  • bg.setVertexIdKey("npi")- 告诉BatchGraph顶点标识符将被存储在一个叫做npi的顶点属性键中。
  • ...sameDayTotal) = line.split(',')*.trim() - 文件中的每一行由一对逗号分隔的值组成。该行将逗号分隔的文本行创建一个列表,其中包含解构为五个变量的五个值。
  • def v1 = bg.getVertex(id1) ?: bg.addVertex(id1)- BatchGraph有助于简化上一节中的getOrCreate功能。BatchGraph覆盖默认addVertexgetVertex功能并允许通过NPI number进行规范和查找顶点。如果没有找到顶点,getVertex将返回null并添加顶点。
  • bg.commit()- 完成加载后,进行最后的commit调用以完成事务缓冲区中的所有剩余元素。

DocGraph示例演示了加载数千万条边的关键策略,总结如下:尽可能预处理数据以减轻加载负担并提高性能,使用BatchGraph以便专注于所加载的数据,而不是加载机制,例如手动批量提交,处理标识符和编写getOrCreate方法。

在这个规模上要考虑的其他一些策略和想法包括:

  • 使用数据子集编程和测试加载脚本以缩短开发周期时间。
  • 使用第三方库来提高工作效率并减少要编写的代码量(例如groovycsv)。
  • 如果数据可以组织起来的,并且条件允许的话,可以考虑一下使用gpars进行并行加载的方法。
  • 如果有倾向于从非JVM语言(如Python)加载数据,可以理清本文思路并在Gremlin中编写加载脚本。通过这种方式,加载数据的过程可以快速完成,从而可以专注于针对Python应用程序开发的语言特定工具(例如Bulbs)。

千万级规模图的处理已经放在了“ Boutique Graph Data ” ,它对于许多应用程序来说是通用的,其中大多数应用程序可以直接启用它。从某种意义上说,这可能是最接近“ 十的次方 ”的规模的应用之一。

结论

本文探讨了向Titan加载较少的数据的情况。在数百万和数千万条边的规模上,我们通常需要Gremlin脚本和REPL来批量加载活动。对于那些刚刚开始使用TinkerPop和Titan的人来说,需要掌握最基本的堆栈知识。适应这种规模的操作非常有助于我们成功探讨本系列下一章将要探讨的数亿甚至数十亿规模的图。

致谢

Vadas Gintautas博士最初预见到需要更好地记录批量装载策略,并且这样的策略似乎很好地将自己分成十的次方

这篇文章最初由Stephen Mallette和Daniel Kuppitz在Aurelius发表。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 100万
  • 1000万
  • 结论
  • 致谢
相关产品与服务
大数据
全栈大数据产品,面向海量数据场景,帮助您 “智理无数,心中有数”!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档