声明
本文的内容完全来自于TigerGraph官方文档及说明,不代表本人实际测试和主观观点
缘起
一切源自于TigerGraph的官方测试文档:https://cdn2.hubspot.net/hubfs/4114546/Collateral/TigerGraph%20Benchmark%20Report.pdf?__hstc=3506223.df3da7db83ea26f0c65b13ba83b36a7f.1530072272958.1530072272958.1530072272958.1&__hssc=3506223.3.1530072272959&__hsfp=318508839
该benchmark测试报告中,说明TigerGraph完爆各种图数据库,因此对其感兴趣,研究之。
概述
TigerGraph之所以如此犀利,根本原因在于NPG(NATIVE PARALLEL GRAPH):并行图处理技术。TigerGraph声称NPG技术综合考虑了存储和计算的性能,能够提供实时图更新并提供了内置的并行计算机制。除此之外TigerGraph还提供了特有的查询语言GSQL(SQL like graph query language),通过GSQL可以实现ad-hoc查询和交互式的大数据分析计算。 通过配合使用NPG和GSQL,可以进行深度关联分析(Deep Link Analytics):可以发现其他图数据库由于过于繁琐或不切实际而无法发现的关联。
因此我们主要针对TigerGraph的两大法宝:NPG和GSQL进行调研说明。
架构
NPG(Native Parallel Graph)
自主研发
NPG是TigerGraph的核心组件,采用C++完全自主研发。除此之外还开发了本次图存储引擎是(GSE:Graph Storage Engine)与图处理引擎(GPE:Graph Processing Engine)协作,从而高效完成数据和算法的处理。GPE旨在基于Map-Reduce计算模型提供内置并行性。
高压缩率
TigerGraph提供高效的数据压缩,从而进一步高效的使用内存和CPU Cache。数据的压缩率与数据本身与数据的结构相关,但是一般情况下都能够达到10倍以上的压缩率。例如1TB的数据经过压缩之后,只有100G。压缩不仅减少了内存占用,还提高了CPU Cache命中率,从而提高了整体查询性能。
MPP计算模型
TigerGraph中的每个点和边可以同时作为计算和存储的并行处理单元。通过这种方式,图不再是静态的数据存储集合,而是一个大规模并行处理引擎。图中的所有的顶点都可以通过相互之间连接的边发送和接收消息。图中的一个点或者边可以存储任意信息,TigerGraph通过充分利用多核CPU和内存计算,在每个点和边上执行并行计算。
图分区
支持一系列的图分区算法。在大多数情况下,对input的数据执行自动分区可以取得不错的效果,而且不需要进行特定的调整和优化。TigerGraph的灵活性允许基于应用特性使用特定的以及混合分区策略,从而达到更高的性能。TigerGraph可以将多个图形引擎作为Active-Active的网络架构并行运行。其中每个图引擎都可以针对不同的应用查询方式采用不同的图形分区算法来服务于同一份数据,从而对每种查询模式都能提供不错的性能。前端服务器(通常是REST服务器)可以根据查询类型将应用程序查询路由到不同的图形引擎。
实时深度分析与操作
目前大部分的图技术将大图的遍历限制在两跳之内,而TigerGraph可以支持到3到10跳,每增加一跳都需要遍历更多的联系和属性。TigerGraph可以达到单台服务器每秒遍历1亿个顶点/边,并且单台服务器每秒可以完成10W次更新。
优势
传统的图数据库是单机模式的,不能集群化部署TigerGraph是世界上第一个也是唯一一个可扩展到多个节点的分布式的并行图数据库。因此从理论上来说TigerGraph是没有数据量的限制的,并且可以通过简单的添加服务器来实现横向扩容。由于TigerGraph本身内置的并行计算特性,因此可以轻松在单台服务器上实现10倍甚至100倍的性能提升,并且可以轻松实现3层到10层深度的关系分析,常规数据库在进行3到10层深度关系探索时会出现超时的情况。NPG可以轻松将数据loading速度提升10倍。
GSQL(SQL like Graph Query Language)
TigerGraph提供了GSQL,GSQL是一种用于进行图形分析的高级且功能完备的查询语言。 GSQL具有类似SQL的语法,可以减少SQL程序员的学习成本,同时也提供NoSQL开发人员首选的MapReduce用法,使用MapReduce的方式,可以实现大规模的并行计算。
GSQL查询的主要对象是顶点和边块。多个查询之间的输入节点、关联以及相邻的节点之间都是相互独立的,这种独立性为TigerGraph的并行性提供了极大的可能性。
为了实现并行计算,TigerGraph将由顶点/边缘计算生成的数据存储在累加器中,可以并行地将聚合计算的结果写入到累加器中。累加器有两种形式。全局累加器将结果收集到一个集中位置,而顶点累加器将某一次块计算中涉及的节点上的计算结果进行累加汇总,除此之外还包括其id为计算已知的任何其他远程节点的计算结果。
GSQL还提供标准的控制流原语,包括if else语句,以及while循环语句等,当然还会包含condition条件,在condition条件中可能会引用到全局累加器中的一些数值,这也就意味着condition中以来的值可能需要在整个查询过程中进行动态的计算。每条GSQL语句都会被拆分成一系列的首尾具有相互关联关系的Blocks,一个Block的输出会成为后续的一个或者多个Block的输入,最终一个GSQL语句生成的所有的Blocks最终会被组织成为一个有向无环图。
GSQL的一个非常强大的功能是它能够定义命名参数化查询,这些命名参数可以作为其他查询的参数而被其他查询引用(支持递归调用和引用)。GSQL的query-calling query功能类似于Oracle PL / SQL和Microsoft SQL Server存储过程功能。
GSQL示例
假设我们有一张图,图中的顶点包含两种类型:Product和Customer。 顶点Product上的属性包括:product name, category和单价。顶点Customer的属性包括:用户SSN,name和地址。在本示例中,customer c 买了一个product p,这个行为在图中的建模方式就是:从C指向P的一条边,边的类型是Bought。 Bought类型的边还带有一些价格和购买数量等属性。
假设我们想要计算出通过出售"toy"类别的商品,总共赚了多少钱,我们需要找到所有类型为bought的边,这些边的源顶点是Customer,目标顶点是Product,且目标顶点的类别为“toy”。针对于找到的每条边,我们通过计算每个产品单位的折扣价来确定销售价格:((1- b.discount/100.0)p.price),算出来价格之后,再乘以购买的数量。请注意如何为每个边缘独立(并行)执行此计算。收入是通过将所有b边缘的销售价格相加得到的,这是通过将c的每个边写入位于c的顶点累加器来实现的。 下面的查询打印出每个用户的名字和赚得的利润:
SumAccum<float> @revenue; (1)
Start = {Customer.}; (2)
Cust = select c (3)
from Start:c -(Bought:b)-> Product:p (4)
where p.category == “toy” (5)
accum c.@revenue += p.price(1-b.discount/100.0)b.quantity; (6)
print Cust.name, Cust.@revenue; (7)
该查询开始首先定义一个顶点累加器,该累加器将会一直持有计算的收益。从(1)中可以看到,每个顶点将配备一个名为'revenue'的累加器,它将浮点值聚合成一个总和(实现为算术+函数)。
第(3)行到第(6)行定义了一系列的边,在FROM子句(第4行)指定了这些边的条件:以Start顶点的集合(在Line(2)中初始化以包含所有Customer顶点)为起点,以Product顶点为终点,并且被标记了‘bought’的所有边。
FROM子句还引入了变量:b绑定到edge本身,c绑定到b的源顶点,p绑定到b的目标顶点。
WHERE子句(第5行)指定一个布尔条件,它确定哪些edge参与计算(即那些指向'toy'类别的Product顶点的edge。我们称这些edge为:allowed edges。
对每个allowed edge执行ACCUM子句(第6行)。它向收入累加器写入右侧表达式的值,该值表示对应于edge的单个销售的收入。
针对每个allowed edge都会执行SELECT子句(第3行)。它指定要添加到查询的输出顶点集合的顶点。示例中输出顶点集分配给变量Cust。