假设有一个社交属性的业务,总用户量在百万级别,并且平均每个用户有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,
顶部输入框可以输入命令,然后点击右上角的三角按钮执行命令,下方会显示执行结果。
Neo4j语法
如果说MySQL的使用流程为“创建表”-“插入数据”-“查询”的话,那么图数据库的使用流程就是“创建节点”-“创建节点间的关系”-“查询”。
节点
在Neo4j中,每个数据都是一个节点,每个节点都有属性,例如新增一个用户A,那么用户A就是一个节点,用户A的姓名叫alien,那么姓名就是节点的属性,并且这个属性的值是alien。
在Neo4j中,没有MySQL中的表的概念,取而代之的是Label,任何一个节点,都需要有一个对应的label,例如刚刚的用户A,他的label可以设置为User,表示他是一个用户。
CREAT - 节点
CREATE用于创建一个节点,命令语法为:
1 | CREATE ( |
其中:
- node-name:节点名称,可选参数;
- label-name:标识这个节点的类型;
- property-name:节点的属性名称;
- property-value:节点属性的值。
例如用CREATE创建一个用户,并且用户的id属性为1,姓名叫alien:
1 | CREATE (:User { id:1,name:"alien"}) |
执行结果:
MATCH
MATCH可以理解为MySQL中的from,语法为:
1 | MATCH ( |
其中:
- 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 | MATCH (u:User) |
CREATE - 关系
到这里之前,我们已经知道如何创建一条数据(节点)了,现在我们需要创建节点之间的关系。
创建关系之前,我们首先要定义“关系”,比如用户A关注了B,那么我们可以将关系定义为“关注”,并且是单向的。并且我们还可以给这个关系增加属性,例如什么时候关注的。
创建一个关系的语法为:
1 | MATCH <node1-name>:<label1-name>, (<node2-name>:<label2-name>) |
其中:
- relationship-name:关系的别名;
- relationship-label-name:关系的label。
例如创建一个关注关系:
1 | MATCH (u1:User), (u2:User) |
执行后,得到如下两个存在关系的节点:
N度人脉
结合文章开头的问题,我们尝试用图数据库来实现。先构造一波数据,构造20个用户,然后随机构造一些关注关系,得到关系图如下:
Spring Boot中已经集成了neo4j:spring-boot-starter-data-neo4j,数据量较多的话,也可以利用这个库,编写简单的NodeEntity和Neo4jRepository就可以实现。
假设要查询u1的5度所有人脉,则查询语句如下:
1 | MATCH |
结果集如下:
性能对比
在《Neo4j in Action》这本书中,作者在关系型数据库和图数据库(Neo4j)之间进行了实验,他们的实验试图在一个社交网络里找到最大深度为5的朋友的朋友。他们的数据集包括100万人,每人约有50个朋友,实验结果如下:
可以看出,对于图数据库来说,数据量越大,越复杂的关联查询,约有利于体现其优势。从深度为4/5的查询结果我们可以看出,图数据库返回了整个社交网络一半以上的人数。