0%

图数据库 - Neo4j初探

假设有一个社交属性的业务,总用户量在百万级别,并且平均每个用户有30个好友,现在需要查询某个用户的N度人脉,那么你会怎么存储这些数据?

一些典型的多级分销业务也与此类似,需要统计某个人的N级邀请关系;也还有更为复杂的电商类业务,例如查询购买了商品A的用户还购买了哪些其他商品。

用数据库?假设用User表存储用户属性,用Friend表存储用户之间的好友关系,然后进行N次查询?如果时间允许的话可以,因为到3度好友关系时,最坏情况下,一个人会有$30^3=27000$个好友了,到4度就$30^4=810000$了。

随着社交,金融等行业的快速发展,他们产生了很多关系数据,而传统的数据存储引擎大多采用列存储,文档存储,Key-Value存储等,数据和数据之间,在存储层面是没有什么关联的(外键引用等也不太算是数据本身关系层面的关联),很难去处理这一类数据,因此以数据之间的关联关系运算为主的图数据库应运而生。

Neo4j是2007年正式发布的一款图数据库,在DB-Engines发布的图数据库排名中,Neo4j以较高的得分领先于其他图数据库,我们以Neo4j为例,学习一下其在关系运算中使用方法。

安装

Neo4j 4.0以上的版本需要JDK11,本机安装3.*的版本;

安装之后,通过neo4j start,启动一个本地的neo4j,通过浏览器访问本地:http://localhost:7474/ ,进入图形化界面,默认账号密码为Neo4j,

1

顶部输入框可以输入命令,然后点击右上角的三角按钮执行命令,下方会显示执行结果。

Neo4j语法

如果说MySQL的使用流程为“创建表”-“插入数据”-“查询”的话,那么图数据库的使用流程就是“创建节点”-“创建节点间的关系”-“查询”。

节点

在Neo4j中,每个数据都是一个节点,每个节点都有属性,例如新增一个用户A,那么用户A就是一个节点,用户A的姓名叫alien,那么姓名就是节点的属性,并且这个属性的值是alien。

在Neo4j中,没有MySQL中的表的概念,取而代之的是Label,任何一个节点,都需要有一个对应的label,例如刚刚的用户A,他的label可以设置为User,表示他是一个用户。

CREAT - 节点

CREATE用于创建一个节点,命令语法为:

1
2
3
4
5
6
7
CREATE ( 
<node-name>:<label-name> {
<Property-name>:<Property-Value>,
...
<Property-name>:<Property-Value>
}
)

其中:

  • node-name:节点名称,可选参数;
  • label-name:标识这个节点的类型;
  • property-name:节点的属性名称;
  • property-value:节点属性的值。

例如用CREATE创建一个用户,并且用户的id属性为1,姓名叫alien:

1
CREATE (:User { id:1,name:"alien"})

执行结果:

2

MATCH

MATCH可以理解为MySQL中的from,语法为:

1
2
3
MATCH (
<node-name>:<label-name>
)

其中:

  • label-name:类似MySQL中的表明;
  • node-name:类似MySQL中的表的别名。

例如:MATCH (u: User),其实可以等效于MySQL中的from User as u

WHERE

条件语句,语法为:

1
WHERE <condition>

condition即为普通条件,例如判断u:User中,id为1的用户,则:

1
WHERE u.id = 1

RETURN

返回的查询字段,类似于MySQL中的select。

MATCH & WHERE & RETURN

这三个命令都没法单独执行,需要组合完成,例如查询用户表中,姓名为alien的用户,则:

1
2
3
MATCH (u:User)
WHERE u.name = "alien"
RETURN u

CREATE - 关系

到这里之前,我们已经知道如何创建一条数据(节点)了,现在我们需要创建节点之间的关系。

创建关系之前,我们首先要定义“关系”,比如用户A关注了B,那么我们可以将关系定义为“关注”,并且是单向的。并且我们还可以给这个关系增加属性,例如什么时候关注的。

创建一个关系的语法为:

1
2
3
4
5
6
7
8
MATCH <node1-name>:<label1-name>, (<node2-name>:<label2-name>)
CREATE (
<node1-name>-[<relationship-name>:<relationship-label-name> {
<Property-name>:<Property-Value>,
...
<Property-name>:<Property-Value>
]-<node2-name>
)

其中:

  • relationship-name:关系的别名;
  • relationship-label-name:关系的label。

例如创建一个关注关系:

1
2
3
4
MATCH (u1:User), (u2:User)
WHERE u1.id = 1 AND u2.id= 2
CREATE (u1)-[r:FOLLOW{at:"2020-05-01"}]->(u2)
return u1,u2

执行后,得到如下两个存在关系的节点:

3

N度人脉

结合文章开头的问题,我们尝试用图数据库来实现。先构造一波数据,构造20个用户,然后随机构造一些关注关系,得到关系图如下:

Spring Boot中已经集成了neo4j:spring-boot-starter-data-neo4j,数据量较多的话,也可以利用这个库,编写简单的NodeEntity和Neo4jRepository就可以实现。

4

假设要查询u1的5度所有人脉,则查询语句如下:

1
2
3
4
5
6
MATCH
(u1:User)-[:FOLLOW]-(u2:User)-[:FOLLOW]-(u3:User)-[:FOLLOW]-(u4:User)-[:FOLLOW]-(u5:User)
WHERE
u1.id = 1
RETURN
u2,u3,u4,u5

结果集如下:

5

性能对比

在《Neo4j in Action》这本书中,作者在关系型数据库和图数据库(Neo4j)之间进行了实验,他们的实验试图在一个社交网络里找到最大深度为5的朋友的朋友。他们的数据集包括100万人,每人约有50个朋友,实验结果如下:

6

可以看出,对于图数据库来说,数据量越大,越复杂的关联查询,约有利于体现其优势。从深度为4/5的查询结果我们可以看出,图数据库返回了整个社交网络一半以上的人数。

参考



-=全文完=-