前面有几篇介绍了jgraph,这个java图论库,这篇我们来学习一个非常流行的图数据库neo4j。
为什么要有图数据库
在传统开发过程中,开发者往往会选择将数据结构规范化,为数据模型设计复杂紧密的关系,然后将其塞进关系型数据库中,直到整个数据结构越来越复杂不像原本的样子。比如,在关系型数据库中表示多对多关系往往需要一些冗余的表。但是这在图数据库中是非常自然直观的。
从数据结构层面来说,图论中的顶点和边可以构建任意图形,表达能力强大。此外图论历史悠久,几十年沉淀的算法都可以直接在图数据库中使用。
此外尤其是社交型场景,比如,我们可能需要查找用户的好友,或者好友的好友。这种情况下,对应传统关系型数据库,我们可能会需要反复join用户表,查多层好友的情况下,关系型数据库就难以胜任了。
有哪些图数据库可以选型呢?
这里有同花顺团队的选型文档
https://www.nebula-graph.com.cn/posts/reason-to-choose-a-graph-database
Neo4j 虽然从选型文档上看来neo4j并没有性能优势,但是在流行度上还是遥遥领先的。
neo4j的特点
ACID
Neo4j的目标是成为一个图形数据库,并把重点放在数据库上。这意味着你将在Neo4j数据库中得到ACID的全部支持:
- A原子性
- C一致性
- I隔离性
- D持久性
支持cypher查询语言
类似于sql,图的增删改查有一套自己的语言。
企业版和社区版
企业版和社区版有一些区别:
1、容量:社区版最多支持 320 亿个节点、320 亿个关系和 640 亿个属性,而企业版没有这个限制;
2、并发:社区版只能部署成单实例,不能做集群。而企业版可以部署成高可用集群或因果集群,从而可以解决高并发量的问题;
3、容灾:由于企业版支持集群,部分实例出故障不会影响整个系统正常运行;
4、热备:社区版只支持冷备份,即需要停止服务后才能进行备份,而企业版支持热备,第一次是全量备份,后续是增量备份;
5、性能:社区版最多用到 4 个内核,而企业能用到全部内核,且对性能做了精心的优化;
6、支持:企业版客户能得到 5X10 电话支持(Neo4j 美国电话、邮件,微云数聚电话、微信、邮件);
区别还是挺大的,尤其是性能上核心的差距和集群化的问题。
访问方式
- neo4j shell
- neo4j web browser
- Java 原生API
- Java Driver API
- Spring Data Neo4j
后面会介绍这些访问方式。
核心概念
跟图论中的顶点(vertex)和边(edge)类似,Neo4j也有节点(Node)和关系(Relation)的概念。此外,在图的实际应用中,作为数据库还能支持给节点保存属性信息(Properties),还可以给节点打标签(Labels)用于减少实际查询扫描的节点数。
上面基本涵盖了Neo4j的核心要素,下面我们实际上手看看。
部署
嵌入式模式
嵌入式的方式类似sqlite将Neo4j完全嵌入到你的java应用中,然后你可以直接用java API访问同JVM中的Neo4j实例。
maven中添加依赖
1 2 3 4 5
| <dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j</artifactId> <version>3.5.35</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10
| public class AppTest {
private static GraphDatabaseService embedded;
@Before public void setup() { File dbPath = new File("target/neo4j-db"); embedded = new GraphDatabaseFactory().newEmbeddedDatabase(dbPath); } }
|
创建节点
1 2 3 4 5 6 7 8 9 10 11
| @Test public void create() { try (Transaction transaction = embedded.beginTx()) { Node n = embedded.createNode(); n.addLabel(Label.label("Student")); n.setProperty("name", "zhangsan"); n.setProperty("age", "23"); transaction.success(); } }
|
createNode
创建节点,addLabel为节点添加标签,setProperty
为节点添加属性。
查询所有节点
1 2 3 4 5 6 7 8 9
| @Test public void list() { try (Transaction transaction = embedded.beginTx()) { for (Node n : embedded.getAllNodes()) { System.out.println(n.getLabels() + " " + n.getAllProperties()); } transaction.success(); } }
|
根据属性查节点
1 2 3 4 5 6 7 8 9
| @Test public void find_property() { try (Transaction transaction = embedded.beginTx()) { try (ResourceIterator<Node> nodes = embedded.findNodes(Label.label("Student"), "name", "zhangsan")) { nodes.stream().forEach(e -> System.out.println(e.getLabels() + " " + e.getAllProperties())); } transaction.success(); } }
|
在两个节点间创建关系
1 2 3 4 5 6 7 8 9
| @Test public void relation() { try (Transaction t = embedded.beginTx()) { Node n1 = embedded.findNode(Label.label("Student"), "name", "zhangsan"); Node n2 = embedded.findNode(Label.label("Product"), "name", "lisi"); n1.createRelationshipTo(n2, RelationshipType.withName("friend")); t.success(); } }
|
执行cql
1 2 3 4 5 6 7 8 9 10 11
| @Test public void cql() { try (Transaction transaction = embedded.beginTx()) { try (Result r = embedded.execute("match (n:Student) where n.name = 'lisi' return n ");){ while (r.hasNext()) { System.out.println(r.next()); } } transaction.success(); } }
|
后面我们会讨论cql的更多细节。
Server模式
大多数neo4j的实际应用还是会选择把neo4j单独部署为图数据库,就是传统的C-S架构。
Rest
首先Neo4j 3.5以后已经废弃了rest方式调用,4.x已经完全移除了
https://neo4j.com/docs/rest-docs/current/
官方还是推荐cypher的方式访问Neo4j。
Java Driver
非常类似jdbc了,完全以cql(sql)访问和操作数据。
maven 添加依赖
1 2 3 4 5
| <dependency> <groupId>org.neo4j.driver</groupId> <artifactId>neo4j-java-driver</artifactId> <version>4.2.0</version> </dependency>
|
创建连接
Driver创建session,对应一个连接。
1 2 3 4 5 6 7 8
| private Session session; private Driver d;
@Before public void setup() { d = GraphDatabase.driver("bolt://localhost:7687", AuthTokens.basic("neo4j", "neo4j")); session = d.session(); }
|
执行cql
1 2 3 4 5 6 7 8
| @Test public void listAll() { List<Record> records = session.readTransaction(t -> { Result run = t.run("MATCH (n) RETURN n AS node"); return run.list(); }); print(records); }
|
对比
- 首先由于neo4j是java开发的,所以嵌入式开发只支持java,但是Server模式能支持大量语言。
- 嵌入式模式由于不需要网络传输的损耗,所以性能对比server模式有一些优势
- 从架构上来看,解耦数据库和应用是非常必要的,能提升应用的扩展性。