摘要: “一分钟,我要这个人的全部信息”,霸道总裁拍了拍你,并提出这个要求。
本文分享自华为云社区《大规模数据如何实现数据的高效追溯》,作者: DevAI。
“一分钟,我要这个人的全部信息”,霸道总裁拍了拍你,并提出这个要求。秘书开始发力,找到了:姓名、年龄、联系方式、爱好,这些信息。不太够?那就再加上亲朋好友信息,近期活动信息,更完整展现这个人。虽然是个段子,但也给与我们一些启示:对象本身的信息可能不够“全”,周边关联的数据也是对象信息的重要组成,这些关联数据对在进行数据分析和挖掘时十分有用。
现实生活中关联关系十分普遍,比如人的社交、商品生产和消费行为之间都是关联关系。数据分析时,为了更好的利用关联关系,常使用图作为数据结构,使用图结构保存数据的数据库被称为图数据库。传统的关系型数据库,以表格视角对数据进行呈现,可以方便的对数据进行查询管理,而图数据库更关注节点和周边节点的联系,是一种网状结构,适用于追溯分析、社交网络分析、异构信息挖掘等等应用。华为云提供的图数据库服务就是GES(Graph Engine Service)[1]。
基于图数据库可以做很多有趣的应用,数据追溯就是一个很常见的应用。数据追溯,就是把各环节产生的数据进行关联与溯源。疫情中,查看商品的流通过程,检查商品是否有可能有接触传染源。测试活动中,通过构建测试过程网络,分析测试活动的完备性,用于进行质量评估。这些都是追溯的典型使用场景。若以传统关系型数据库构建数据追溯,需要独立构造和维护多个关系表,并实现多对多的关系网络,不易于理解复杂的业务逻辑,与此同时,也会伴随着追溯查询实现复杂和查询缓慢的问题。
图1 关系型数据库和图数据库对比
用一个例子简单说明图数据库在数据分析领域的优势。图1是一个简单的选课系统,记录了学生选课以及相应的课程信息。如右图所示,我们根据图数据库的表达方式把这些信息转化为一张图。可以看出,图可以更加直观地表达选课和班级等关系,清楚地呈现实体之间的关系,更方便进行关联分析。比如,根据图我们可以很容易找到和小布一起上数学课的同学,也可以快速找到选课兴趣相同的同学。通过图数据库可以很方便查询到周边节点信息,非常适用于追溯实现。那如何基于图数据库如何实现追溯服务?接下来我们将以华为云GES为例,分析基于GES图数据库追溯服务的实现和优化。
什么是图
在图数据库中,图由以下部分组成:
- 点:图中的实体对象,在图中表现为一个节点。例如,社会的人,流通的商品等都可以抽象为图中的一个节点。
- 边:图中节点与节点之间的关系。如人与人的社会关系,商品的购买行为等。
- 属性:用于描述图中节点或者边的属性,比如编号、名称等。聚类和分类分析中,权重是常常作为关系属性,也就是边的属性。
图2 有向图与无向图
根据边是否有方向,可以把图分为有向图和无向图。对于有向图来说,边的起点和终点是确定的。图2中,城市是一个节点,城市间的距离和城市之间交通方式为边。城市交通就是一个有向图,不同方向交通方式用不同的边表示,而城市间距离是无向图,因为距离和方向无关。GES使用时,需要将点和边处理成不同的对象,点边都需要定义需要的属性。点主要就是包含实体的信息,而边需要指定起点与终点。
定义GES图
GES建立图的步骤可以参考官方文档[2]。主要就是对节点和边进行定义,将数据处理为点和边文件,最后导入GES中,可通过界面或API导入。处理无向图时,即不区分边的起点和终点,通常也会设定一个默认方向,即指定边的起点和终点,这是为了处理和导入数据方便,在实际查询中可以忽略这种方向设定。
在GES构建图的过程中,定义点和边以及相关属性的文件被称为元数据。点和边的类型被称为label,每个label可具有多个属性,如上文提到的名称、权重等,都可以作为点或边的属性。在GES中,label一旦定义并创建成功将不被允许修改,如果必须要修改label定义,就需要格式化图并重新创建导入元数据文件到图中。
节点通常是由现实中的实体抽象而来,GES节点属性常用的数据结构包含了float、int、double、long、char、char array、date、bool、enum和string等。通常来说节点中,字符串类型的属性较多,非字符串属性可以根据数据类型进行选择。字符串类型有两个选择:string和char array。char array有数据长度限制,通常为256,而string类型没有长度限制。但是在GES中使用char array更有优势,这是因为char array数据存放在内存中,string类型数据存放在硬盘中,因此char array查询效率更高,这也是GES元数据定义需要注意的地方。在我们项目的场景中,节点的名称和编号都是常用的查询条件,综合考虑属性特征,如节点名称较长而节点编号较短,最终名称使用了string类型,而编号选择了char array类型。
GES查询优化
定义好节点信息后,可以在图中进行查询。GES使用的是Gremlin[3]进行查询。Gremlin是一个开源的流式查询语言,查询实现灵活,不同图数据库对查询语句的分解以及优化处理都不相同,因此,不同的写法可能查询效率可能不同。接下来我们就一种追溯查询场景进行分析。
图3 多分支查询场景分析
如图3所示,字母代表label,也就是一种节点类型。可以看到该场景具有较多查询分支,按照图中的节点要求,Gremlin查询语句直接实现如下:
g.V(id).hasLabel('A').ouE().otherV().hasLabel('B').ouE().otherV().hasLabel('C').as('c').outE().otherV().hasLabel('F').outE().otherV().hasLabel('H').select('c').outE().otherV().hasLabel('D').as('d').outE().otherV().hasLabel('G').select('d').outE().otherV().hasLabel('H')
基于当前Gremlin,GES Gremlin server会将查询分解为多个查询原子操作,并由GES engine·执行。对于这种多跳的复杂查询,会解析为较多的原子操作并频繁交互,这会导致的查询效率低下。对于这种场景,考虑使用optional语句进行查询,效率会得到提升。查询语句如下:
g.V(id).hasLabel('A').ouE().otherV().hasLabel('B').ouE().otherV().hasLabel('C').as('c').optional(outE().otherV().hasLabel('F').outE().otherV().hasLabel('H')).optional(select('c').outE().otherV().hasLabel('D').as('d').optional(outE().otherV().hasLabel('G')).optional(select('d').outE().otherV().hasLabel('H')))
optional在一定程度上可以降低分支的查询范围,从而提升查询效率。在项目实际使用中,使用optional可以提升查询性能1倍左右。但是optional不是所有场景都适用,Gremlin实现需要根据查询场景、数据规模和数据特点进行优化处理,例如图中节点的稀疏程度和分支的数量都是可以考虑优化的点。
在对GES查询优化时,即使对Gremlin语句进行了优化,也有可能达不到期望的查询性能。这是因为使用Gremlin时,处理查询过程中Gremlin server解析后的原子操作可能会和GES engine频繁交互,反而会降低查询性能,而且针对Gremlin查询优化处理范围也有限。虽然Gremlin是图数据库通用的查询脚本定义方式,但是各个厂家对于Gremlin脚本优化处理不同,因此更推荐使用GES原生API。原生API针对固定场景做了更多的优化,并且减少了Gremlin解析处理过程,因此性能更优,但同时也引入了通用性和效率之间的平衡问题,毕竟API没有通用的定义实现。
下面我们将介绍几种常见的追溯查询场景。这些场景都可以通过Gremlin查询实现,但是如果通过使用GES系统API,可以获取更好的查询性能。
场景(1) 追溯某个节点前(后)n层节点
该查询较为常见,主要用于查询某个节点的父子节点,对于图1 的场景可以找到班级的所有同学,该场景Gremlin实现如下:
g.V(id).repeat(out()).times(n).emit().path()
这种场景下,推荐使用GES算法文档中的k-hop算法解决该问题,需要注意,这个算法接口只会返回满足查询条件的子图中的所有点,但没有节点详情和边信息,如果需要节点详情可以采用batch-query批量进行节点详情查询。如果需要边信息,推荐场景(2) 使用的API。
场景(2) 按条件追溯某个节点之前(后)n层节点,节点筛选条件相同
g.V(id).repeat(outE().otherV().hasLabel('A')).times(n).emit().path()
这种场景下,推荐使用repeat-query方法。该方法可以快速实现某个起点前后n跳查询,并且可以限定节点查询条件,并且所有点的查询过滤条件相同。在查询中,如果不同的点需要使用不同的查询条件进行过滤,可以先不指定点查询条件,待返回查询结果后再进行过滤。不指定点的查询场景可以退化为场景(1),并且该API可以同时返回节点和边的详情。
场景(3) 按条件追溯某个节点之前(后)n层节点,不同节点筛选条件不同
图3的例子就是一个这样的场景,每层的查询label不同。这种情况下,推荐使用filtered-query进行查询,该方法需要详细指定每个节点的过滤属性,相当于将每个查询条件都在参数中一一指定,实现完全满足条件的查询。项目中,相对于Gremlin 查询,filtered-query的查询性能可以提升10倍左右。
上述三个场景中repeat-query和k-hop具有更好的泛化能力,可以随意指定查询跳数n,需要设定的参数简单。而filtered-query需要详细指定查询中每层节点的属性,参数较为复杂,具体使用中可以根据业务需求进行选择。
GES还提供了很多算法,如Node2vec, subgraph2vec,GCN算法,本文只介绍了基于GES进行节点快速查询并提供追溯服务,后续也会考虑如何基于建立好的图,进行一些数据节点融合,也可以进行相似度分析、质量评估和流程推荐等,更好地挖掘数据的价值。
文章来自PaaS技术创新Lab,PaaS技术创新Lab隶属于华为云,致力于综合利用软件分析、数据挖掘、机器学习等技术,为软件研发人员提供下一代智能研发工具服务的核心引擎和智慧大脑。我们将聚焦软件工程领域硬核能力,不断构筑研发利器,持续交付高价值商业特性!加入我们,一起开创研发新“境界”!
PaaS技术创新Lab主页链接:https://www.huaweicloud.com/lab/paas/home.html
【参考资料】
- 华为云GES产品介绍: https://support.huaweicloud.com/productdesc-ges/ges_04_0001.html
- 华为云GES用户指南: https://support.huaweicloud.com/usermanual-ges/ges_01_0009.html
- Gremlin官方文档:https://tinkerpop.apache.org/docs/3.3.11/