更多干货内容请关注微信公众号“AI 前线”,(ID:ai-front)
当前,基于图结构的数据(以下简称为“图数据”)无处不在。Facebook、Twitter 和 Pinterest 等企业已经看到并最大化地利用了这些相互关联数据的强大之处。这直接导致了对图数据解决方案关注度的提升,各种解决方案也与日俱增。
Redis 可加载模块系统的推出,使我们意识到在 Redis 工具集中引入图数据结构的巨大潜力。Redis 采用原生 C 语言实现,侧重于提供高性能特性。现在我们通过开发 Redis,为其新引入了图数据库能力。RedisGraph 以开源项目提供在 GitHub 上。
本文将介绍 RedisGraph 的一些内部设计和特性,并展示其当前所具备的能力。
RedisGraph 是一种基于 Redis 全新设计实现的图数据库。它使用了新的 Redis Modules API,扩展 Redis 并提供了一些新的命令和功能。其主要特性包括:简单且快速的索引和查询、在内存中存储数据、使用了定制的内存高效数据结构、基于磁盘的数据持久化、以数据表形式给出的结果集、支持简单并广为使用的图查询语言 Cypher,以及数据的过滤、聚合和排序等。
为介绍 RedisGraph 的一些关键特性,下面我们给出一个基于 redis-cli 工具运行的例子。
实体通常表示为图中的节点。本例创建了一个小规模的图,图中的实体为演员和电影,以“参演”关系连接参演电影的演员和电影。使用 GRAPH.QUERY 命令发布 CREATE 语句的格式如下,该语句实现将新的实体和关系添加到一个图中。
下面一条命令创建了一个图:
RedisGraph 实现了 openCypher 图查询语言的一个子集。尽管其中仅支持该语言的数个功能,但是对于从图中抽取有用的信息而言,这些功能是完全够用的。使用 GRAPH.QUERY 命令执行查询的命令格式为:
GRAPH.QUERY <graph_id> <query>
下面我们在所创建的电影图数据上执行一系列查询。
其中,第一行是结果集的头部信息,按 RETURN 语句命名各个列。第二行中包含了查询的结果。
第二个查询是找出每位演员参演了多少部电影。
不同的图数据库,可能采用了不同的结构表示图。有的使用了邻接列表,也有的使用了邻接矩阵。每种表示结构都有其自身的优劣之处。对于 RedisGraph 而言,关键在于给出一种支持对图做快速搜索的数据结构。我们使用了一种称为“Hexastore”的结构保存图中的所有关系。
Hexastore 的结构由一系列三元组组成。其中,每个三元组包括如下三部分:
-
主语(Subject);
-
谓词(Predicate);
-
目标(Object)。
主语表示源节点,谓词表示关系,目标表示目的节点。对于图中的每条关系,Hexastore 将保存源节点、关系边和目的节点间所有可能存在的六种排列。以下面这条关系为例:
(Aldis_Hodge)-[act]->(Straight_Outta_Compton)
其中, Aldis_Hodge 是源节点,act 是关系,Straight_Outta_Compton 是目标节点。
该关系的六种可能排列如下:
SPO:Aldis_Hodge:act:Straight_Outta_Compton
SOP:Aldis_Hodge:Straight_Outta_Compton:act
POS:act:Straight_Outta_Compton:Aldis_Hodge
PSO:act:Aldis_Hodge:Straight_Outta_Compton
OPS:Straight_Outta_Compton:act:Aldis_Hodge
OSP:Straight_Outta_Compton:Aldis_Hodge:act
一旦构建了 Hexastore,我们可以轻易地实现图搜索。例如,如果要查找“Straight Outta Compton”电影中的演员,那么可以实现为搜索 Hexastore 中所有包含前缀 OPS:Straight_Outta_Compton:act:* 的字符串。
如果要查找 Aldis Hodge 参演的所有电影,那么可以实现为查找所有包含前缀 SPO:Aldis_Hodge:act:* 的字符串。
尽管 Hexastore 会占用大量的内存,因为每条关系表示为六个三元组,但是这样的三元组数据结构不仅支持快速搜索,而且可以高效地使用内存,因为它并不会重复地生成已有的字符串前缀。
目前已经存在着一些图查询语言,我们并不想重新造轮子,实现我们自己的语言。因此,我们决定实现最广为使用的图查询语言 openCyper 的一个子集。尽管 Open-Cypher 项目提供的语言解析器创建方法易于使用,但我们还是决定创建我们自己的解析器。该解析器使用 Lex 作为分词器,并使用 Lemon 生成 C 目标解析器。
正如上面所提及的,我们目前只支持该语言的一个子集。我们将会继续添加一些新能力,并扩展语言。