图数据库实战
上QQ阅读APP看书,第一时间看更新

第 1 章 初识图

本章内容

● 图以及相关术语介绍

● 图数据库如何有助于解决高度关联数据问题

● 图数据库相对于关系数据库的优势

● 识别适合使用图数据库的问题

现代应用程序是基于数据构建的,而数据的规模在不断扩大,复杂性也在不断提高。随着数据的复杂性越来越高,我们对应用程序从这些数据中有所收获的期望也越来越高。如果你接触计算机有一定年头了,那么可能还记得那些加载数据需要很长时间并且功能有限的应用程序。时代不同了,现在的应用程序需要提供强大、灵活、即时的数据洞察力。但是,现代应用程序每解答100个疑问,最常见的数据工具(关系数据库)只能很好地处理其中的88个,还剩下12个难以解答。这些疑问涉及数据中的链接和连接,而这些方面恰恰能让我们对数据产生强大而独特的理解。这就使我们处在了一个岔路口:要么选择使用关系数据库这个“锤子”1强行解答这些疑问,勉强应付;要么后退一步,看看还有哪些工具可以更好、更快、更省力地做出解答。

1这里的“锤子”来自于查理·芒格的一句话:“拿着锤子的人,看什么都像钉子。”——译者注

既然选择阅读本书,就说明你已经决定从关系数据库这个“锤子”后退一步,研究一条鲜为人知的道路:图数据库。本书面向想探索其他方法来解决高度关联数据相关问题的开发人员、工程师和架构师。我们假定你熟悉关系数据库,并且对了解图数据库何时、何地以及如何能成为更好的工具感兴趣。

本书的目标是帮你掌握所需的技术,将图数据库添加进你的工具袋里。我们多么希望当初在开始构建以图为基础的应用程序时能有这样一本书作为指导。本书将展示常见的图模式,这些模式突出了图数据库是如何以传统关系数据库难以实现的方式来导航和探索数据的。

我们的主要方法是构建一个虚构的餐厅评价和推荐应用程序示例,名为“友聚”(DiningByFriends)。通过这个示例来走一遍从规划、分析、设计到实现的软件开发生命周期,我们将演示如何设计和使用图数据。每一章都建立在前一章的基础之上,到本书结束时,我们将在一个图数据库上创建了一个能够正常运行的应用程序。我们认为,通过解决一系列现实问题(即使这些问题被简单化了),立即将概念付诸实践,是掌握新技术的最佳途径。让我们从介绍什么是图和图数据库,以及它们与关系数据库等传统工具的区别开始吧。

1.1 什么是图

当你查看路线图,研究组织结构图,或者使用Facebook、LinkedIn、Twitter等社交网络的时候,就是在使用图。图,是一种几乎无处不在、用来思考现实世界场景的方法,因为图能够抽象出这些场景要表现的项(item)和关系,从而能够快速、高效地处理数据中的连接。

让我们用一个常见的任务来演示这一点吧:从家去超市。拿出一张纸,画出从你家到超市的路线,很可能如图1-1所示。

图1-1 表示从家到超市的导航图

图1-1展示了一张用抽象方法来表示关键项及其关系的图。我们首先抽象出关键位置,如路口,并将其表示为圆;然后将这些关键交叉点之间的连接表示为线,以体现其间的关联。这只是如何自然地将现实世界问题表现为图的一个例子。

对现实世界中的实体及其关系进行抽象是人类的天性,这种抽象结构的数学名称就是。当要思考的数据集含有大量高度相互关联的项时,也可以将该数据集描述为一个由相关事物组成的网络,这也是图的另一种说法。

在地图上,城市通常用圆来表示,连接这些城市的路则用线来表示。在组织结构图中,一个圆通常代表一个人,并通常带有相关的头衔,将这些人连接在一起的线则表示雇佣关系。在社交网络中,人们通过加好友或关注的方式来相互连接。这种对实体及其之间的关联进行概括的过程就是图和图论的基础。数学家几个世纪以来对图进行的定义和研究,让我们得以直接将图论中的这些定义作为术语。

:顶点和边的集合。

顶点:图中零条、一条或多条边经过的点,也称为节点或实体。

:图中两个顶点之间的关系,有时也称为关系、链接或连接。

欧拉与图论的起源

人们一般认为,图论起源于莱昂哈德·欧拉在1736年发表的一篇关于“哥尼斯堡七桥问题”的论文。哥尼斯堡(现为俄罗斯加里宁格勒)当时是普鲁士的一座城市,位于普雷格尔河畔。这条河中有两座岛,通过七座桥相互连接以及和主城连接。论文中的实验要设计出一条路线,让镇上的居民可以不重复、不遗漏地一次走完七座桥,最后回到出发点。欧拉通过创建岛(顶点)和它们之间的桥梁(连接或边)的抽象表示来解决这个问题。基于这种抽象,欧拉指出,重要的不是具体的项,而是表现这些项之间如何连接的拓扑结构。

欧拉在论文中指出,要解决这个问题,该图需要零个或两个具有奇数连接的节点。如今,任何满足这一条件的图都被称为欧拉图。如果路径只访问每条边一次,则该图具有欧拉路径。如果路径起点和终点相同,则该图具有欧拉回路,或称为欧拉环。我们讲这个故事是为了分享一个很有趣的历史背景,但是在实践经验中,我们从未在任何现实世界的问题中用过这些学术事实或欧拉定义。

虽然定义很正式,但是图的优点是简单、易于说明。在实际使用中,图通常由表示顶点的圆和表示边的线组成,如图1-2所示。

图1-2 很容易通过用圆来表示顶点、用线来表示边来表示图

注意 本书使用顶点这两个术语。有一些图数据库使用节点而非顶点,使用关系而非边,但是它们在概念上是相同的。

对于软件开发人员来说,图并不是什么新概念。它是我们在软件开发中使用的许多常见数据结构的基础,甚至可能根本令人意识不到。常见的数据结构,如链表和树,都是应用了特定规则的图。虽然这些数据结构为开发人员所熟知,但是它们特定于图的实现细节通常被抽象掉了。

图数据库是一种数据存储引擎,将包含顶点和边的基本图结构与持久化技术和遍历(查询)语言相结合,以创建针对高度关联数据的存储和快速检索进行优化的数据库。与其他数据库技术不同,图数据库建立在这样的概念上:实体之间的关系与数据中的实体同等重要,甚至比后者更加重要。由于实体和关系得到了同等对待,因此图数据库可以用于更准确、更容易地表示和推理现实世界中的关系,特别是与其他数据库技术相比。正如本书将展示的,如果既要表示事物之间丰富多样的关系,又要识别基于这些关系的模式,那么图数据库是更好的工具。

下面简要介绍用关系数据库表示多种关系面临的一些挑战。关系数据库(这个命名有点讽刺)在表示丰富的关系方面表现得相当差。它使用外键来表示关系,而外键就是指向其他表中主键的指针。这些指针不容易观察和操作。与图数据库不同,在查询关系时,外键是从一行跟随到另一行的。(因此,关系数据库尽管能够表示关系,但往往代价高昂。)关系数据库中的查找表或链接表使用的不是查询时的指针结构,而是存储了有关关系属性的结构,类似于图数据库中的边结构。

图数据库为在数据中遍历关系提供了极好的工具。通过将连接(边)和数据项(顶点)放在同等重要的地位,图数据库可以将这些关联表示为数据库里的完整结构,从而轻松地观察和操作。这种存储丰富关系的能力是图数据库更适合处理复杂链接数据用例的主要原因之一。用开发人员的话来说,边和顶点一样,都是“一等公民”。也就是说,关系在数据模型中与事物或实体一样重要、一样有用。

最后要说的是,图数据库可以通过其他技术无法实现的方式来提高开发人员解决某些问题的效率。图数据库存储数据的方式能更好地代表现实世界,从而令开发人员能够更容易地思考和理解他们所处的业务领域。这能令新的团队成员更快地熟悉业务,并同时学到业务领域知识及其数据库表示。

虽然本书主要讨论图数据库,并主要使用关系数据库作为衬托,但我们应该注意,数据库世界并非只有这两种类型的数据存储。从最广泛的角度来说,数据库可以分为以下五种引擎类型。图1-3总结了这些数据库引擎类型之间的关系。

键值数据库:所有数据都由唯一标识符(键)和关联的数据对象(值)表示。例子包括Berkeley DB、RocksDB、Redis和Memcached。

列存储数据库(又名面向列、宽列式数据库):数据按列来存储,可能每行有大量的列,也可能每行的列数不一样。例子包括Apache HBase、Azure Table Storage、Apache Cassandra和Google Cloud Bigtable。

文档数据库(又名面向文档数据库):将数据存储到带有唯一键的文档中,该文档可以具有不同的模式,也可以包含嵌套数据。例子包括MongoDB和Apache CouchDB。

关系数据库:将数据存储在包含具有严格模式行结构的表中,允许在表之间连接行来建立关系。例子包括PostgreSQL、Oracle Database和Microsoft SQL Server。

图数据库:将数据存储为顶点(节点、组件)和边(关系)。例子包括Neo4j、Apache TinkerPop的Gremlin Server、JanusGraph和TigerGraph。

图1-3 按数据复杂性排序的数据库引擎类型

从这些例子可以看出,默认情况下,只有关系数据库和图数据库才具有将数据中的实体关联起来的功能。虽然使用键值数据库、列存储数据库或文档数据库的特定产品也可能具有这个功能,但通常是由具体数据库产品厂商特意添加的增强功能。因为我们的重点是图数据库,而只有关系数据库具有类似、可比较的功能,所以接下来的讨论仅限于这两种引擎类型。

作为开发人员,我们经常选择熟悉的工具,而不是最佳的工具,尤其是在处理数据库时。大多数开发团队对关系数据库的各种细节有深入的了解,但是很少有人精通其他类型的数据库。因此,即使某些问题可以用更好的工具解决,我们也经常出于方便或者无知而默认使用关系数据库。

这并不是说关系数据库是糟糕的工具。实际上,它通常是我们在开发应用程序时的首选工具。但是,关系数据库有其局限性。虽然关系数据库也可以用于存储高度关联数据,但在许多情况下,可以使用专门为这类用例设计的工具来简化工作。在以下三个方面,图数据库提供了比关系数据库更简单、更优雅的解决方案,本节将一一介绍:

● 递归查询(例如,组织中员工的汇报层次结构或者组织结构图)

● 复合结果类型(例如,订单和产品报告示例)

● 路径(例如,过河问题)

本章将用三个示例来分别展示图数据库的这三个独特功能。从第2章开始,我们将介绍“友聚”的问题领域,并开始正式的数据建模过程。届时,大多数示例将随对应示例领域的发展而变化。但在此之前,我们将使用多种方法介绍图和图数据库的基本概念。

递归查询

递归查询会连续执行多次,反复调用自己,直到满足某种终止条件。关系数据库不能很好地处理递归操作(尤其是无边界的递归操作),不论在语法还是性能方面都很吃力。这通常会导致需要编写和维护复杂的查询语句,或者/以及对数据进行过度的反规范化,而这只是为了及时返回结果而已。

图数据库能够利用其表示丰富关系的能力,干净、高效地处理无边界递归查询。为了更好地理解,让我们看看递归查询在SQL和图数据库中分别是什么样子的。假设有如图1-4所示的公司员工和经理列表,我们来研究如何找出具体某个人的汇报层次结构。

图1-4 演示递归查询用的管理层次结构

为了在关系数据库中对该层次结构进行建模,以下SQL语句定义了表结构。然后,按照这个表结构来布局数据,如表1-1所示。

CREATE TABLE org_chart (
  employee_id           SMALLINT NOT NULL,
  manager_employee_id   SMALLINT NULL,
  employee_name         VARCHAR(20) NOT NULL
);

表1-1 使用关系数据库的组织管理层次结构示例

然后使用递归函数来查询这些数据,以找出用户的管理层次结构。以下是查询语句的代码片段。

WITH RECURSIVE org AS (
     SELECT employee_id,
            manager_employee_id,
            employee_name,
            1 AS level
     FROM org_chart
  UNION
     SELECT e.employee_id,
            e.manager_employee_id,
            e.employee_name,
            m.level + 1 AS level
     FROM org_chart AS e
       INNER JOIN org AS m ON e.manager_employee_id = m.employee_id
  )
SELECT employee_id, manager_employee_id, employee_name
FROM org
ORDER BY level ASC;

如果用SQL的通用表表达式(common table expression,CTE)编写过像上面管理层次结构查询一样的SQL语句,那么你就会知道,这些表达式可能编写和调试起来很复杂,并且因性能不佳而臭名昭著。嵌套查询和递归查询(如前面的层次结构示例所示)是图数据库擅长解答的疑问类型。例如,图1-5展示了相同数据的图结构。

图1-5 以圆为顶点、箭头为边的组织层次结构图表示

要在图中找到用户的管理链,需要编写一个类似于SQL查询的查询语句,这种查询在图中称为遍历。对于我们的层次结构示例,遍历语句如下所示。

g.V().
  repeat(
    out('works_for')
  ).path().next()

注意 这个遍历使用的是一种称为Gremlin的图查询语言,整本书中都将使用该语言。此时还不需要确切地了解它是如何工作的,我们将从第3章开始详细研究它。现在,只需要注意该查询语句与前面的SQL查询语句相比更为简单。

这个示例展示了通过图来解决递归问题有多么简单、直接。如果将其与图1-5进行比较,就可以看到,这种遍历能够自然地契合我们对数据层次结构的直观理解。
 

复合结果类型

你是否曾经需要从数据库中返回一个包含好几种数据类型的结果集?虽然可以通过联合各个表的列来实现这一点,但结果往往不太理想。图数据库的优点之一就是能够返回包含不同数据类型的结果集。让我们比较一下在返回不同数据类型结果集时,关系数据库和图数据库分别是如何处理的。

例如,假设我们有一个订单处理系统,并且希望在结果集中既返回订单信息,又同时返回产品信息。图1-6展示了通过表来做到这一点的关系数据库传统实现。

图1-6 关系数据库中的订单和产品表。注意列名之间的差异

以下代码片段展示了检索带有相关产品信息的订单查询语句2。表1-2展示了该查询语句的结果集。

SELECT id,
       name,
       address,
       null AS product_name,
       null AS cost,
       'Order' AS object_type
FROM Orders
UNION
SELECT id,
       null AS name,
       null AS address,
       product_name,
       cost,
       'Product' AS object_type
FROM Products;

表1-2 检索订单并从中检索产品的关联查询结果

可以看到,联合两种不同数据类型的结果表明,这个方案会包含大量空值(这种情况通常称为稀疏数据稀疏矩阵)。如此多的空数据是两个表之间的列不一致造成的。关系数据库指定联合返回的结果集必须包含一致的列。在稀疏数据的情况下,这不仅增加了返回的数据量,还降低了数据结构的描述性。下面来看看相同数据在图数据库中的显示方式(见图1-7)。

图1-7 订单产品信息示例展示为图中的顶点(未对边建模)

使用这个图,可以编写一个图遍历来返回产品和订单数据。在此示例中,图数据库返回以下结果。

gremlin> g.V().valueMap(true)

==>[label:order, address:[123 Main St], name:[John Smith], id:1]
==>[label:order, address:[234 Park St], name:[Jane Right], id:2]
==>[label:product, cost:[10.76], id:234, product_name:[widget 2]]
==>[label:product, cost:[5.95], id:123, product_name:[widget 1]]

与前面的SQL结果相比,从图返回的数据保留了对象的含义及其表示的语义,并且没有多余的空数据。因为图数据库提供了返回不同类型数据的灵活性,所以当要处理不同的数据类型时,我们可以写出更简洁的代码。
 

路径

路径是一组顶点和边的序列,描述遍历如何在图中移动。例如,在Google地图或Apple地图中,两个地点之间的一组方向就是路径。从数据库中返回两个对象的关联关系是图数据库独有的功能。

让我们看看一个被称为“过河问题”的经典问题,以此说明路径是如何以一种新颖的方式来解决问题的。在问题中,有一只狐狸、一只鹅和一袋大麦必须由农夫用船运过河。但是,移动过程有如下限制条件。

● 每次移动过程中,除了农夫以外,船上只能携带一件物品。

● 农夫必须参与每次移动过程。

● 狐狸不能单独和鹅待在一起,否则狐狸会吃掉鹅。

● 鹅不能单独和大麦待在一起,否则鹅会吃掉大麦。

使用关系数据库,如果不采用暴力破解的方法来计算出所有可能的组合,我们将找不到解决该问题的方法。但是,如果使用图,通过巧妙的数据建模和强大的寻路算法(又名路径算法),就能很简单地解决这个问题。

首先,将系统的初始状态建模为图的顶点。我们将顶点命名为TGFB_,其中每个字符代表问题的一部分:

● T(船和农夫)

● G(鹅)

● F(狐狸)

● B(大麦)

● _(河)

顶点TGFB_编码了问题的状态,告诉我们船和农夫、鹅、狐狸和大麦都在河的一侧。我们期望达到的状态是,所有这些字符都在河的另一侧。

当用顶点来表示可能的状态时,就能使用边来展示如何从一个状态转换到下一个状态了。例如,图1-8展示了如何表示农夫把鹅运到河的另一侧,把狐狸和大麦留在河最初一侧的状态变化。图1-9展示了将所有的潜在选项建模为这些状态(顶点)和状态变化(边)的表示结果。

图1-8 农夫用船(T)运鹅(G)过河(_),留下狐狸(F)和大麦(B)的图示

{%}

图1-9 使用寻路算法求解过河问题的完整图。注意,这清楚地描述了可能的解决方案,任何违反限制条件的状态都被突出显示了

图1-10说明了如果通过删除违反限制条件的状态(顶点)和相邻关系(边)来简化图,会发生什么情况。可以通过删除任何连接回先前状态的边来进一步简化图,因为它们会将我们引向先前的状态(这种情况在图论中称为)。

{%}

图1-10 用只显示有效状态的寻路算法求解过河问题

通过分析图1-10,可以看到有两条不同的路径能够达到我们想要的状态。要查询图以返回这些路径,只需利用图数据库的寻路功能通过如下所示的遍历语句来返回这两条正确的路径。

g.V('TFGB_').
  repeat(
    out()
  ).until(hasId('_TGFB')).
  path().next()

当我们运行这个遍历语句时,它不仅返回访问过的第一个和最后一个顶点,而且返回一路上访问过的所有顶点和边。这两个列表代表了解决方案的两条不同路径。

TFGB_ -运鹅过河-> FB_TG -空船返回-> TFB_G -运大麦过河-> F_TGB -运鹅返回->
     TFG_B -运狐狸过河-> G_TBF -空船返回-> TG_FB -运鹅过河-> _TGFB

TFGB_ -运鹅过河-> FB_TG -空船返回-> TFB_G -运狐狸过河-> B_TFG -运鹅返回->
     TGB_F -运大麦过河-> G_TBF -空船返回-> TG_FB -运狐狸过河-> _TGFB

这个例子虽然只是一个数学问题,但是代表了现实世界应用中的许多同类问题,例如在地图上寻找路线,在物流系统中寻找最优资源使用解,或者在社交网络中定位人与人之间的联系。这些情况从根本上都是求解从一个实体到另一个实体的最佳操作集。图数据结构能够让我们充分利用它的寻路功能,而其他数据库类型并不原生支持这些功能。

2虽然实际工作中没有人会用这么笨的SQL和方法来解决问题,但是这个例子能明显体现关系数据库与图数据库的差异。请把注意力集中在图数据库如何优雅地处理关系数据库里的UNION操作上。——译者注

1.2 我的问题适合用图数据库吗

从社交网络分析、推荐引擎、依赖性分析、欺诈检测和主数据管理,到搜索问题和互联网上的研究,你很快就能列出一个适合使用图数据库的用例列表。使用该列表的困难在于,除非你的问题恰好在列表中,否则很难知道如何使用图数据库来解决你的问题,或者你的问题是否适合用图数据库来解决。

本节不再关注特定的用例,而是以更通用的方式来研究问题。这有些偏概念性,但是我们发现很难通过一个例子概括特定的问题领域。我们将从定义一个通用问题开始,然后提供一些示例来进行说明,并用一个评估问题的通用框架和一个用于决定是否使用图数据库的决策树(这也是图的一种形式!)来结束本节。

在阅读互联网上关于图数据库的大量信息时,你可能会遇到这么一种说法:“一切问题都是图问题。”我们同意,现实世界很容易用图术语来描述,但是说用一种类型的数据库就可以解决所有问题则过于简单化了。一个问题可以用图来表示,并不一定意味着图数据库是解决这个问题的最佳技术。

我们的流程将从一个简单的疑问开始:我们要解决什么技术问题3?回答这个疑问可以提供相关业务问题的关键细节,这些细节将决定我们需要存储的数据类型以及如何检索数据。技术问题可以归为以下几类:

3本书中的问题分为两类:一类是业务问题,比如地图求解;另一类是具体实施中的技术问题,如搜索、聚合、模式匹配,等等。——译者注

● 选择/搜索

● 获取数据相关性或递归数据

● 聚合

● 模式匹配

● 中心性、聚集性和影响力

让我们依次研究每一类,并讨论哪些适合使用图数据库,哪些不适合。

选择/搜索

以下类型的疑问可归类为搜索或选择问题。这些疑问侧重于寻找一组具有共同属性(如姓名、位置或雇主)的实体。

● 给我X公司所有员工的名单。

● 系统中有哪些人叫John?

● 找到N千米范围内的所有商店。

解答这类疑问不需要数据中丰富的关系。在大多数数据库中,只需要使用单个筛选条件或者索引就能解答这类疑问。虽然可以使用图数据库来解答,但这类疑问并不需要图数据库特有的功能。相反,建议使用诸如PostgreSQL之类的本地数据库或诸如Apache Solr和Elasticsearch之类的搜索技术。要解答上述疑问,这些数据库或工具要么更成熟(例如RDBMS),要么经过更好的优化(例如搜索工具)。因为这类问题并没有利用数据中的关系,所以根据我们的经验,承担图数据库的额外复杂性是不值得的。

结论 对于这类疑问,使用RDBMS或搜索技术。

 

获取数据相关性或递归数据

探究实体之间关系的疑问增加了数据的意义并提供了拓扑价值,为图数据库提供了一个强大的用例。下面是这类疑问的几个例子。

● 认识X公司高管最简单的方法是什么?

● John和Paula是如何认识的?

● X公司与Y公司有什么关系?

图数据库比任何其他类型数据引擎都能更好地利用这些信息,而且图数据库查询语言更适用于推断数据内部的关系。虽然关系数据库也能解决这类问题,但是这类“朋友的朋友”查询需要复杂的SQL并且难以维护,或者需要对多个表之间的递归CTE代码或复杂连接进行推断。

结论 对于这类疑问,使用图数据库。

 

聚合

数据聚合查询是关系数据库的一个优秀用例。关系数据库经过优化,能以最小的开销快速执行复杂的聚合查询。下面是这类疑问的几个例子。

● 系统中有多少家公司?

● 过去一个月里,我的平均日销售额是多少?

● 系统每天处理多少笔交易?

这些类型的查询可以在图数据库中执行,但是图遍历的本质要求接触更多的数据。这会导致更高的查询延迟和更多的资源消耗。

结论 对于这类疑问,使用RDBMS。

 

模式匹配

基于实体关联方式的模式匹配是充分利用图数据库功能的一个主要例子。这类查询的典型用例包括推荐引擎、欺诈检测和入侵检测。下面是这类疑问的几个例子。

● 系统中谁的资料和我相似?

● 这笔交易是否与其他已知的欺诈交易相似?

● J. Smith和Johan S.是同一个用户吗?

模式匹配用例在图数据库中非常常见,以至于图查询语言具有特定的内置功能来精确地处理这类查询。

结论 对于这类疑问,使用图数据库。

 

中心性、聚集性和影响力

一个实体相对于另一个实体的影响力或重要性是图数据库的典型用例。下面是这类疑问的几个例子。

● 在LinkedIn上与我有联系并且最有影响力的人是谁?

● 在我的网络中,哪个设备发生故障时影响最大?

● 哪些部件可能会同时出现故障?

这类疑问的例子还包括在Twitter网络中找到最有影响力的人,识别基础设施的关键部分,或者定位数据中的实体组。求解这类问题需要考虑实体、实体之间的关系、事件关系和邻近关系。与模式匹配用例一样,通常有特定的内置图查询语言功能来处理这类问题。

结论 对于这类疑问,使用图数据库。

要确定图数据库是否是解决你手头问题的理想选择,到目前为止所讨论的问题类型提供了重要的第一步。但如果你的问题与这些预定义类型都不一样,那又该怎么办呢?本节将使用带有决策框架的“朋友的朋友”问题来帮助你决定一个问题是否适合用图数据库解决。

下面使用如图1-11所示的一个小型社交图来演示,其中Alice、Bob、Ted和Josh是顶点,并由边进行连接。要解答的疑问是:给定图中的一个人,这个人关注的人又关注了其他人,那么在这些“其他人”中,有谁是第一个人可能也想关注的?这个疑问和LinkedIn、Twitter或Facebook等网站每天向用户推荐连接时解答的一样。首先把它分成四个基本部分。

● 给定图中的一个人。

● 找出这个人所关注的人。

● 再找出这些人又关注了其他哪些人。

● 在这些“其他人”中,谁是第一个人可能也想关注的?

图1-11 展示了常见“朋友的朋友”模式的简单社交图

以Bob为起点(第一个点)。Bob关注了Alice(第二个点)。Alice关注了Ted和Josh(第三个点)。因此,Bob可能想关注Ted和Josh(终点)。

请看图1-12中的决策树,该树旨在回答“是否应该使用图数据库”的疑问。然后按照该决策树逐步检查并分析为什么这些疑问会导致你在工作中使用或不使用图数据库。从一开始就应该注意到,这里关注的是事务性用例(如在线事务处理,简称OLTP)。对于分析性用例(例如在线分析处理,简称OLAP),决策矩阵可能会有所不同。本书前10章几乎只关注事务处理案例,但最后一章会给出关于图处理(或图分析)的一些指导。

图1-12 “我应该使用图数据库吗”决策树。从最高层开始,最终会回答“是”“否”或者“也许”

我是否像关心实体本身一样关心实体之间的关系,甚至更关心后者

这个问题也许是最关键的线索,因此我们把它放在第一位。它揭示了图数据库一大功能的核心:关系和实体一样有意义。如果我们对这个问题的回答是肯定的,那么可能需要一个允许使用复杂关系表示形式的数据模型,因此使用图数据库是这个业务问题的绝佳选择。但如果答案是否定的,那么另一个类别的数据引擎也许会是更好的选择。

就“朋友的朋友”问题而言,答案是肯定的。在第一个基本部分(给定图中的一个人)之后,每部分都需要使用人与人之间的关系来求解。
 

我的SQL查询是否需要在同一个表上执行多个join,或者需要递归的CTE

虽然SQL查询中包含大量join可能表明图数据库是一个很好的选择,但并不能确定。SQL查询中的大量join通常是数据模型规范化的良好标志。但是,当这些join并非用于检索引用数据(如关系数据库中的第三个范式所做的那样),而是用于将记录项链接在一起(就像父子关系那样)时,就可能需要考虑使用图数据库了。此外,当我们不知道要执行的join数量时,使用图数据库的递归查询模式更好。

以“朋友的朋友”为例,假设我们要解答“从Bob到Ted有哪些连接”的疑问。在关系数据库中执行该查询需要未知数量的join,并且可能无法完成(即表明两者之间不存在路径)。但是,图数据库可以高效地对诸如此类的无边界分层数据进行递归。如果递归方法有助于解决问题,则通常最好使用图数据库。
 

我的数据结构是否会不断变化

我们不会把图数据库称为与数据模式无关的(schemaless),该术语表示数据库引擎不会对写操作强制执行数据结构检查。我们知道有几个图数据库产品确实会强制执行数据结构检查,但是你可以把图数据库设计得能够容忍不断发展变化的数据。另外,关系数据库因其对数据结构的严格性和更改数据结构时的复杂性而闻名。

如果你的问题需要接收具有不同数据结构的数据(例如依赖关系管理),那么可能值得研究使用图数据库来解决问题。数据结构的灵活性不能作为选择图数据库的充分理由,但与其他功能相结合,很可能足以使你转而使用图数据库。
 

我的业务领域是否天生适合使用图数据库

如果要进行诸如路由、依赖关系管理、社交网络分析或聚类分析等操作,那么你的问题就与高度互联数据有关,你的业务领域也就可能非常适合使用图数据库。提醒一下:即使业务领域模型天生适合使用图数据库,但是如果你的具体技术问题并不依赖于图中的关系来求解,那么也应该考虑其他选择。

实际上,早在2014年,我们对图数据库的最初研究就揭示了客户数据为何非常自然地适合使用图数据库4。我们甚至尝试了三个不同的图数据库产品。做出来的模型具有内置的继承功能、多跳遍历和对依赖关系分析的自然要求。客户应用程序中的两个主要数据结构甚至被称为组件(顶点的别名)和关系(边的别名)。它应该使用图数据库而不是关系数据库构建,这一点对于所有粗略浏览过数据和业务领域的人来说都是显而易见的。

然而,该特定客户的正确解决方案并不是使用我们评估过的三个图数据库引擎,而是使用它们的关系数据库(或者说,以与其主要访问模式一致的方式来使用它)。然后,我们将局部优化过的关系投影(基本上是旧模型的完整副本)添加到为执行查询而设计的关系数据库架构中。这个模式有时被称为命令查询责任分离(CQRS)模式。通过这个新的“快速读取”模型,一些最苛刻查询的性能提高了100倍。

起初,我们都为图数据库并没有提供必要的性能提升而感到震惊,因为按照业务领域数据建模自然而然地适合使用图数据库。然后,我们更加仔细地研究了用于评估数据库性能的五个查询。除了继承建模之外,所有查询都不需要图式访问模式。因为图不是必需的,所以我们使用激进的反规范化来处理继承用例。实际上,所需的访问模式非常适合关系数据库。因此,当对数据进行建模并利用RDBMS查询优化器的优势时,性能得到了显著提高。

回到前面的图数据库决策树(见图1-12),如果其中一个或多个问题的回答为“是”,则可能适合使用图数据库。如果你仍然不确定(仍然觉得使用图数据库有风险),那么可以先花两天到两周时间做一个小项目试试,将图数据库作为解决方案的一部分进行评估。另外,并不一定全部使用图数据库,也并不一定全部不使用图数据库。不要害怕尝试用图数据库来解决问题的一部分。使用图数据库的多模型方法(即混合使用关系数据库和图数据库)很常见,并且根据我们的经验而言,往往非常成功。

正如本章开头提到的那样,关系数据库能够很好地解决100个应用程序疑问中的88个,因此可以放心使用。剩下的12个疑问实际上是你可能想开始实验图数据库的地方。本书的剩余部分(从第2章的数据建模开始)将介绍使用图数据库构建软件的方法和原因。

42016年7月,在西雅图GraphDay举办的“Graph DB Shootout 2.0”研讨会上,我们利用公共数据集重新进行了分析。

1.3 小结

● 图数据库的基础是离散数学的图论部分,后者已经有数百年历史了。这意味着数学家们花费了几个世纪的时间来创建术语,但并非所有术语都有用,也并非所有术语都与使用图数据库构建软件有关。

● 图由顶点(也称为节点或实体)和边(也称为关系、链接或连接)组成。边在顶点相交。

● 数据库的五种常见类型是键值数据库、列存储数据库、文档数据库、关系数据库和图数据库。在这五种数据库中,只有关系数据库和图数据库能够对任意复杂程度的关系进行建模。

● 图数据库将关系设计成“一等公民”,这使构建依赖于这些关系的软件变得更加容易。当要解答严重依赖于数据之间关系的疑问时,图数据库往往比其他类型的数据库表现得更好。

● 对于需要诸如递归查询、返回不同结果类型或返回事物之间路径等特性的用例,使用图数据库更容易编码,并且性能更好。

● 由于图数据库的强大功能和灵活性,互联网上有很多图用例可参考,其中有好有坏。判断一个用例是好还是坏的最重要因素是对要解答的疑问有深入的了解。