dot 速度的未来正在您的城市举办的活动中到来。

加入我们参加 Redis 发布会

RedisGraph 和 Redis:什么、为什么以及如何

像许多软件工程师一样,我喜欢玩 龙与地下城。我喜欢提升我的角色并面对越来越强大的敌人。这样做需要金币和经验。而获得这些东西的最佳方式是传统的地下城探险。

如果您需要了解龙与地下城的背景信息,此视频 可能会有所帮助。简短但过于简化的版本:在龙与地下城中,您探索地下建筑群,在其中与怪物战斗,并夺取他们的金币。

不幸的是,地下城没有为像我们这样的冒险者提供便利。有时怪物很强大,但没有宝藏。有时宝藏随处可见。有时房间是空的。那么我们如何找到地下城中我们想要寻找的宝藏所在的位置呢?

解决此问题的有趣方法是使用 图数据库,例如 RedisGraph,将地下城表示为图。从数据的角度来看,地下城是实体(房间、怪物和宝藏)及其关系的集合。图非常适合建模这种类型的数据。使用图数据库,我们可以查询地下城的图,以找到将提升我们角色等级的可怕生物和闪闪发光的物品。

也许您不熟悉图和图数据库?那么,加入我一起冒险,探索什么是、为什么以及如何。

什么是图数据库?

图数据库实际上非常容易理解。我认为它们实际上比关系数据库更容易理解。但是,如果您在关系领域花费了大量时间(我们中的许多人都有,包括我自己),您可能需要忘掉一些东西。只需将所有关系数据放入大脑的不同区域,并腾出空间让新想法涌入。

完成了?好的。让我们深入了解图数据库!

图数据库包含一个 ,它由 节点 组成。关于图、节点和边的解释可能有点抽象,因为从根本上说,它们都是相当抽象的概念。因此,我将使用示例来使它们更加具体。

节点是数据的名词,是事物。它们有一个标签,告诉您它们是什么类型的。它们还可以具有属性,提供有关节点的更多信息。让我们看看几个带有标签和属性的节点

下面我们有两个节点。第一个节点的标签为“房间”,它只有一个属性,告诉我们有关该房间的信息。在这种情况下,它的名称是:“食人魔国王的巢穴”。第二个节点的标签为“怪物”,它有两个属性,一个是告诉我们怪物的名字是“拉尔夫食人魔国王”,另一个是杀死他可以获得 1200 点经验值。节点非常简单,有点像 Java 或 C# 等编程语言中的对象。它们具有类型和属性。

现在,让我们添加一条边,看看会发生什么:这条边具有“包含”的类型和从房间到怪物的方向。它的目的是在房间和怪物之间建立关系。类型是这种关系的本质,在许多方面类似于节点的标签。我喜欢认为边是动词——更确切地说是及物动词——它们将名词连接在一起:房间包含一个怪物。这在节点之间添加了一个关系。

边的方向是任意的。无论哪种方式,它都建立了关系。我也可以创建一条类型为“被包含”的边,指向相反的方向。但这样我的句子就会变成:怪物 被包含在 房间中。这是被动语态。而且,正如我多年前的英语老师教我的那样,要避免使用被动语态,因为它更加冗长且难以理解。

总的来说,这些节点和边被称为图。最简单(也可能是最不有趣的)图根本没有节点。当然,如果没有节点,它就不能有边。

另一方面,图可以变得非常复杂。节点可以有多条边指向它们和从它们发出。一对节点甚至可以有多条边连接它们。节点也可以是孤立的,没有边!看看那张怪物图!它显示了三个房间、一扇密门和一堆宝藏。以及名为拉尔夫的守护者。

它们有什么用?

我一直在忙着为我荒谬的地下城示例建模,包括它所有的连接房间、秘密、怪物和宝藏。如果您正在构建一个基于文本的在线游戏,比如 MUD,这将是建模其状态的好方法。

但是大多数开发人员并没有构建 80 年代的基于文本的游戏。我们大多数人都在构建更实用的东西。图数据库可以解决哪些实际问题?各种问题。以下是一些示例

  • 家谱:节点是人,关系是,好吧,就是关系。
  • 物流:节点是原点、消耗点以及介于两者之间的所有点,而关系是货物转移。
  • 交通运输:节点是交通枢纽——例如火车站、汽车站和地铁站——而关系是连接它们的路线。
  • 社交媒体:节点是帐户,关系是,同样,关系,例如朋友、关注或屏蔽。

这些都是图数据库的良好候选者,因为它们具有复杂的关系,讽刺的是,这些关系难以使用关系数据库进行建模。这是图数据库的主要优势之一:它们能够非常出色地建模关系。

但图数据库还有另一个重要优势:它们没有模式。这使得它们在投入生产后更容易使用。例如

  • 节点有一个标签描述它们是什么。创建具有新标签的新节点很容易。只需编写代码来执行此操作。在关系数据库中,这将需要一个新表。
  • 节点具有属性,提供有关该节点是什么的详细信息。在节点中添加新属性很容易。在关系数据库中,这类似于添加新列。
  • 边有一个类型描述关系的本质。在图数据库中添加新类型的边很容易。在关系数据库中,这将需要一个新的映射表(对于多对多关系)或添加外键(对于一对多关系)。

您甚至可以想象将一对多关系转换为关系数据库中的多对多关系吗?使用图数据库,您无需关心一对多和多对多。事物相互关联即可。如果需要关联,则添加一条边。仅此而已。

我可以用 Redis 做到吗?

可以。您可以。您可能熟悉 Redis 模块。模块是您可以安装以扩展 Redis 功能的扩展——通常是通过添加新命令和数据结构,但有时会更多。Redis 创建了多个模块,它们添加了各种功能。其中一个,RedisGraph,提供了一个图数据库的数据结构。

我觉得在没有代码的情况下写博客文章不太合适,因此我将展示一些使用 Cypher 与图交互的示例,Cypher 是 RedisGraph 使用的查询语言。我将使用 RedisInsight 来执行此操作,因为它有一个很酷的视觉化工具,值得一试,但如果您愿意,也可以使用 redis-cli。

注意:如果您确实使用 redis-cli,请确保所有 Cypher 查询都以 GRAPH.QUERY key “您的 Cypher 查询在这里”开头。

  1. 让我们做一些基本操作,并添加一个节点

> CREATE (:monster { name: 'Ralph the Ogre King', xp: 1200 })

括号之间的内容是待创建的节点,冒号之后,monster 是节点的标签。节点的属性紧随标签,并以非常类似 JavaScript 的方式格式化。

现在我们已经创建了一个怪物,让我们查询它

> MATCH (m:monster) RETURN m

这里的 MATCH 匹配所有带有 monster 标签的节点,并将它们分配给 m。在这种情况下,m 中只有一个节点。毫不意外,RETURN 返回我们告诉它的内容。

这本来可以更简单。由于我们的图中只有一个节点,我们不需要检查标签

> MATCH (n) RETURN n

此查询返回所有节点。

Cypher 很酷,因为它查询看起来有点像一个图。由于节点以圆圈表示,当我们CREATEMATCH 它们时,我们会用括号将它们括起来,以暗示一个圆圈。当我们使用边创建节点时,这个概念会延续下去。

让我们从一个空图开始,并创建一个图,其中 Ralph 在他的巢穴中

> CREATE (:room { name: 'The Den of the Ogre King' })-[:contains]->(:monster { name: 'Ralph the Ogre King', xp: 1200 })

这里我们正在创建两个节点:Ralph 和他的巢穴。但我们还在它们之间定义了一个关系,一个边,它看起来像一个带标签的箭头。箭头指向关系的方向,方括号包含关系的类型。在这种情况下,房间包含一个怪物

我们可以像以前一样查询这个结构,但现在也要包含边

> MATCH (r:room)-[c:contains]->(m:monster) RETURN r, c, m

并且,由于我们的图只包含两个节点和一个关系,我们可以进行更简单的查询

> MATCH (n1)-[e]->(n2) RETURN n1, e, n2

此查询返回所有节点和所有边。

RedisGraph 有很多东西,包括更多更复杂的查询。您应该深入研究 文档,因为里面有很多有趣的东西。但是,对于我们这些寻宝者来说,以下是一个有用的查询: 

> MATCH (r:room)-[:contains]->(:monster)-[:guards]->(:treasure) RETURN r

总结

我们已经看到了图数据库可以解决的各种问题,并且我们已经了解了如何创建和查找节点和边。但它是如何在内部工作的?好吧,这是一个很大的问题,我不会回答。相反,我想把你送到 文档,在那里你可以了解稀疏邻接矩阵、矩阵乘法和 GraphBLAS

最后,我鼓励您更深入地探索充满宝藏的 RedisGraph 领域。 安装它。构建一些酷的东西。与世界分享。如果你构建了一个 MUD,我可以玩吗?