图数据库VS关系数据库-学习图数据库如何工作

1 篇文章 0 订阅
1 篇文章 0 订阅

图数据库VS关系数据库-学习图数据库如何工作

原文翻译自:

Ljubica Lazarevic:Graph Databases VS Relational Databases – Learn How a Graph Database Works

graph

如果您对图数据库以及它们与关系数据库管理系统的比较感兴趣,那么这个初学者友好的指南适合您。

在本文中,通过使用一个小型电影数据集,您将发现图的强大功能。它基于Neo4j Sandbox上的内置数据集和指南。

想自己运行试一试吗?请做!您将在这里找到如何启动和运行的说明。

我们将在这篇文章中介绍什么

图数据库越来越受欢迎和采用。由于来自许多不同来源的数据越来越多,能够理解数据并了解它们之间的联系至关重要。

如果您想了解更多关于图形数据库帮助解决的问题,以及如何发现一个好的应用程序,这里有一篇介绍性的博客文章。

链接:Spotting a Graph-Shape Problem

继续阅读的有些人可能听说过图形数据库 (GDB),有些人可能还没有。 在本文中,我们将准确介绍它们是什么,以及它们如何与更传统的关系数据库管理系统 (RDBMS) 进行比较,后者在过去 40 多年里一直是坚定的软件应用程序。

受 Neo4j 作为图形查询的指导介绍使用的一个小电影数据集的启发,我们将并排查看数据模型或查询在图形数据库和关系数据库中的示例和等效项。

在本文中,我们将:

1. 介绍图形数据库,简要介绍现有的两种模型
2. 从概念上看一下关系范式和图范式之间的区别
3. 通过电影数据集,从GDB和RDBMS的角度比较对比数据模型
4. 比较和对比一些基于 Cypher(用于 GDB)或 SQL 的查询
5. 通过电影示例中出现的更有趣的查询进行讨论,并准确说明正在发生的事情

如果您想在阅读本文之前(或期间!)尝试一下示例演练电影数据集,我们非常欢迎您这样做。你可以在下面这篇文章找到更多。

链接:Getting Started with :PLAY Movies

什么是图数据库?

首先,在我们深入了解图形数据库是什么之前,让我们定义一下这个术语。 图形数据库是一种“不仅是 SQL”(NoSQL)数据存储。 它们旨在以图形结构存储和检索数据。

使用的存储机制因数据库而异。一些GDBs可能使用更传统的数据库结构,例如基于表的,然后在顶部有一个图形API层。

其他的将是“原生”GDBs—这些图数据库从存储、管理、查询的整体架构都维持数据的图结构。当前可用的许多图形数据库通过将实体之间的关系视为一等公民来实现这一点。

不同类型的图数据库

GDB大致有两种类型,资源描述框架(RDF)/三元存储/语义图数据库和属性图数据库。

RDF型GDB使用三元组的概念,它是由三个元素组成的语句:主谓宾。

主语将是图中的资源或节点,宾语将是另一个节点或文字值,谓词表示主题和对象之间的关系。节点或关系上没有内部结构,所有内容都由 URI 形式的唯一标识符标识。

这种结构背后的动机是交换和发布数据。 要了解有关此结构的更多信息,我建议您参考 Jesus Barrasa 在该领域的工作。

链接:Jesus Barrasa:Neo4j is your RDF store

Property GDB(属性图数据库)专注于存储接近逻辑模型的数据的概念。这反过来将基于所寻求的数据本身的问题,并着重于使该表示尽可能高效地用于存储和查询。

与基于 RDF 的图不同,节点和关系上有内部结构,可以提供丰富的数据表示和相关元数据。

以下两张图并排比较了属性图数据库和 RDF 图中表示的样本数据——这两个图都代表了电影阿波罗 13 号中扮演吉姆·洛弗尔角色的汤姆·汉克斯(Tom Hanks)。

图:RDF example of Tom Hanks in Apollo 13
RDF example of Tom Hanks in Apollo 13
图:Property Graph example of Tom Hanks in Apollo 13

Property Graph example of Tom Hanks in Apollo 13

属性图数据库刨析

对于本文的其余部分,我们将重点关注原生属性图数据库,特别是 Neo4j。我们来看看原生属性图的主要组件。

属性图数据库的主要组成部分如下:

  • Node:在图论中也称为顶点(vertex),是构建图的主要数据元素。
  • Relationship:在图论中也称为边(edge),是两个节点之间的链接。它将有方向类型(type)。允许没有关系的节点,不允许没有两个节点的关系。

图:Node and Relationship
Node and Relationship

  • Label:定义一个节点类别,一个节点可以有多个lable.
  • Property:丰富节点或关系,不需要空值!

图:Label, Type, and Property
Label, Type, and Property

图形数据库 vs 关系数据库

关系数据库回顾

许多开发人员都熟悉传统的关系数据库,其数据存储在模式(schema)定义良好的表中。

表中的每一行都是离散的数据实体。行中的这些元素之一通常用于定义其唯一性:主键。它可以是一个唯一的ID,也可以是一个人的社会保险号。

然后我们通过一个称为规范化的过程来减少数据重复。在规范化中,我们将引用移动到另一个表中,例如一个人的地址。因此,我们在表示“某人”这个实体的行到表示“该人”的地址的行之间建立了引用。

最后当我们查询的时候,我们想要重构这个归一化的数据。我们执行所谓的JOIN操作。

在我们的主要实体行中,我们有标识实体 ID 的主键,比方说这个人。我们还有一个所谓的外键,它代表地址表中的一行。我们通过它们的主键和外键连接两个表,并使用它来查找地址表中的地址。这称为 JOIN,这些 JOIN 在查询时和读取时完成。

当我们在关系数据库中执行 JOIN 时,它是一个集合比较操作,我们在其中查看我们的两组数据在哪里重叠(在本例中,集合是人员表和地址表)。在高层次上,这就是传统关系数据库的工作方式。

图:An example of the tables found and how they map to each other in a relational database for an insurance database
An example of the tables found and how they map to each other in a relational database for an insurance database

图:The equivalent example of the insurance database in a property graph database
The equivalent example of the insurance database in a property graph database

原生图数据库的工作原理:连接Connections和无索引邻接Index-Free Adjacency

让我们快速浏览一下原生图数据库及其工作原理。

我们谈到关系数据库中的离散实体是表中的一行。在原生图形数据库中,该行相当于一个节点。它仍然是一个离散的实体,所以我们仍然有这个规范化元素。

一个节点将是一个实体。如果我们有很多人员的节点,我们会为每个人创建一个节点。同时我们也关于这些节点的一定程度的唯一性,比方说社会保障号。

然而,关键的区别在于,当我们将这个人节点连接到另一个离散实体时——例如,一个地址——我们在这两点之间创建了一个物理的连接(也称为关系relationship)。

上述的这个地址会有一个指针,表示连接到该节点的关系的出部是什么?(意思是这个地址节点是与之相连的关系的出部),然后,关系有指向其他节点的入部的指针。(表示关系的另一端为关系的入部,连接着其他节点)

因此,实际上,我们正在收集一组指针,这是这两个实体之间物理连接的体现。 这是最大的不同。

在关系数据库中,您将在读取时使用连接重构数据,这意味着在查询时,它会尝试找出事物如何映射在一起。
在图数据库中,因为我们已经知道这两个元素是相连的,所以我们不需要在查询时查找映射。我们所做的就是跟踪其与其他节点间所存储的关系。

这就是我们所说的无索引邻接。 这种无索引邻接的概念是理解原生图形数据库与其他数据库系统相比性能优化的关键。

Movie Graph简介

我们已经谈到了图形数据库和关系数据库之间的理论差异。 现在让我们开始看一些对比。

电影图由一个数据集组成,该数据集包含演员、导演、制片人、编剧、评论家和电影等子集,以及关于他们如何相互联系的信息。

该数据集在 Neo4j 浏览器中可用,并且可以使用 :PLAY movies 命令轻松触发。 提醒一下,这里有一个博客向您展示如何开始。

Getting Started with :PLAY Movies

电影数据集包括:

  • 133个人节点/实体
  • 38个电影节点/实体
  • 253 上述实体之间的关系/连接,描述连接如:
    • 导演电影的人(DIRECTED)
    • 演过电影的人和演过的角色(ACTED_IN)
    • 写电影的人(WROTE)
    • 制作电影的人(PRODUCED)
    • 评论过电影并给出评分和摘要的人(REVIEWED)
    • 追随另一个人的人(FOLLOWS)

虽然它是一个相对较小的数据集,但它全面地描述了图(Graph)的力量。

比较二者的数据模型

首先我们来看一下二者各自数据库的数据模型。与所有数据模型一样,它们的结构形状最终取决于您提出的问题类型。因此,假设我们要问以下类型的问题:

  • 一个人演过什么电影?
  • 一个人与哪些电影有联系?
  • 一个人合作过的演员都有哪些?

基于这些问题,以下是相关潜在的数据模型:

图:Entity Relationship data model for movie graph
Entity Relationship data model for movie graph
图:Property Graph data model for movie graph
Property Graph data model for movie graph

你会立即发现一些东西——那些 ID 不见了!当我们一知道那里有连接马上就将数据连接在一起时,我们不再需要那些 ID,也不再需要那些映射表来让我们知道不同行的数据如何相互关联。

比较二者的查询方式

现在让我们继续比较一些查询上的差别。以 :PLAY movie示例中的几个第一个查询为例,让我们一起并排比较一下 Cypher 查询,以及等效的 SQL 查询分别是什么样的。

什么是 Cypher,你可能会问? Cypher 是一种图形查询语言,用于查询 Neo4j 图形数据库。 还有一个 OpenCypher 版本,它被许多其他的图数据厂商使用。

链接:Cypher Query Language

链接:What is openCypher?

当我们通过查询在graph中探索时,随着图形数据库以及查询语言帮助探索关系开始发挥作用,查询将开始变得更加清晰。让我们从查寻Tom Hanks开始吧!

如何查询到 Tom Hanks

Cypher:

MATCH (p:Person {name: "Tom Hanks"})
RETURN p

SQL:

SELECT * FROM person 
WHERE person.name = "Tom Hanks"

如何找到 Tom Hanks 的电影

Cypher:

MATCH (:Person {name: “Tom Hanks”})-->(m:Movie)
RETURN m.title

SQL:

SELECT movie.title FROM movie
INNER JOIN movie_person ON movie.movie_id = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
WHERE person.name = "Tom Hanks"

如何找到 Tom Hanks 执导的电影

Cypher:

MATCH (:Person {name: "Tom Hanks"})-[:DIRECTED]->(m:Movie)
RETURN m.title

SQL:

SELECT movie.title FROM movie
INNER JOIN person_movie ON movie.movie_id = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
INNER JOIN involvement ON person_movie.involve_id = involvement.involve_id
WHERE person.name = "Tom Hanks" AND involvement.title = "Director"

如何找到 Tom Hanks 的合作演员

Cypher:

MATCH (:Person {name: "Tom Hanks"})-->(:Movie)<-[:ACTED_IN]-(coActor:Person)
RETURN coActor.name

SQL:

WITH tom_movies AS (
    SELECT movie.movie_id FROM movie
    INNER JOIN person_movie ON movie.movie_id = person_movie.movie_id
    INNER JOIN person ON person_movie.person_id = person.person_id
    WHERE person.name = "Tom Hanks")
SELECT person.name FROM person
INNER JOIN person_movie ON tom_movies = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
INNER JOIN involvement ON person_movie.involve_id = involvement.involve_id
WHERE involvement.title = "Actor"

使用 Cypher 进行更多查询

希望您能了解 Cypher 和 SQL 查询之间的区别。 也许您也很高兴了解更多关于它们的信息! 我们将在博文中进一步提供一些参考资料。

现在,让我们看看您可以在 :PLAY movie 图示例中找到的其他一些 Cypher 查询,并解释发生了什么。

没有任何电影图能够独立于典型的培根数问题(Six Degrees of Kevin Bacon)而存在,我们的电影图表也不例外!

Six Degrees of Kevin Bacon问题:任何参与好莱坞电影业的人都可以在六个步骤内通过他们的电影角色与培根自己联系起来。游戏的名字参考了“六度分离”,这个概念假设地球上的任何两个人相隔六个或更少的熟人而存在连系。

到目前为止,我们所看到的示例每次都遍历某种关系。我们可以很容易地利用那些“joins on write”来遍历许多关系来回答有趣的问题。

回到培根数问题,以下查询将从 Kevin Bacon 人物节点开始,然后从该起点跳出最多 4 步,以返回所有连接的电影和人物。

MATCH (bacon:Person {name:"Kevin Bacon"})-[*1..4]-(hollywood)
RETURN DISTINCT hollywood

我们可以通过在查询模式的关系部分使用 *1…4 的语法来做到这一点:

  • * 表示任何,所有
  • 1…4 表示范围—— 1 表示从 1 开始,4 表示最多 4 步远

我们可以在这个电影数据集上做的另一件关于图的事是两个节点之间的最短路径。

在这个例子中,让我们找出 Kevin Bacon 和 Meg Ryan 之间的最短路径。你会发现我们再次对关系模式使用 * 语法——表示一切。

p= 对你来说可能是新的。你已经看到我们如何对节点使用引用(例如我们当前查询中的 Bacon 或 Meg),我们可以对关系做同样的事情。

我们还可以对全路径(即涉及的所有节点和关系)进行引用。我们为此使用的语法是 refName=,在这个例子中是 p=

我们还使用了 Cypher 函数 shortestPath()——这是一个简单的最短路径函数,它将返回两个指定节点之间的第一条最短路径。请注意,可能还有另一条同样长度的最短的路径,但这个简单的函数只会返回遇到的第一个路径。

对于那些对其他路径相关函数感兴趣的人,请查看 APOC 和 GDS 中可用的函数。

MATCH p=shortestPath(
(bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"}))
RETURN p

给大家一个警告:您可能会看到 [*] 并试图在不受 shortestPath() 函数或 1..4 范围约束的情况下运行您的图形。但这很可能会导致意想不到的结果。

在我们与 Kevin Bacon 和 Meg Ryan 的示例中,即使在这个非常小的数据集中只有 253 条关系,节点和关系之间的所有可能路径组合也很容易在 Bacon 和 Ryan 之间遇到数百万条不同的路径。

在您的关系中使用 * 作为查询的一部分时,请格外小心! 这个问题不会影响最短路径函数的计算,因为当遇到比当前识别的最短路径更长的潜在路径时,它会立即被丢弃。

一个简单的推荐查询

这里有两个查询真正展示了图形数据库的强大功能,我们可以轻松地使用数据中的连接来做一些推荐。

在我们的第一个查询中,我们正在为汤姆·汉克斯寻找新的合作演员,以便与跟他尚未合作过的人合作。查询通过以下方式执行此操作:

  • 首先,找到他所有合作过的演员
  • 然后,找到所有的合作演员的合作演员(简称合-合作演员)
  • 接下来,我们要排除那些已经与汤姆合作过的合-合作演员,并确保合-合作演员不是汤姆本人
  • 最后,我们返回建议的合-合作演员姓名,我们将按照与他们合作过的合作演员的数量对它们进行排序——与该合-合作演员合作过的合作演员越多,推荐越符合。

代码:

MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors),
(coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cocoActors)
WHERE NOT (tom)-[:ACTED_IN]->()<-[:ACTED_IN]-(cocoActors) 
    AND tom <> cocoActors
RETURN cocoActors.name AS Recommended, count(*) AS Strength 
    ORDER BY Strength DESC

很好,因此我们找到了一些潜在的合作演员。在下一个查询中,我们想推荐汤姆克鲁斯作为汤姆汉克斯合作的潜在新合作演员。但是,谁来介绍这些 Tom 互相认识呢?回到电影图中去。

在这个查询中,我们:

  • 找到汤姆·汉克斯的合作演员,然后找出这些合作演员中哪些也与汤姆·克鲁斯合作过
  • 然后我们将返回合作演员以及他们与汤姆汉克斯和汤姆克鲁斯一起出演的电影

代码:

MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors),
(coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cruise:Person {name:"Tom Cruise"})
RETURN tom, m, coActors, m2, cruise

写在最后

我们已经完成了电影数据库示例的演练。 希望那些有关系数据库背景的人能更好地了解关系数据库和图形数据库之间的相似点和不同点,以及对 Cypher 查询语言的品味。

如果您热衷于了解有关建模和查询 Neo4j 的更多信息,请查看免费的 Graph Academy。

感谢原作者 Ljubica Lazarevic 的分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值