Neo4J 2|学习笔记

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
日志服务 SLS,月写入数据量 50GB 1个月
简介: 快速学习 Neo4J 2

开发者学堂课程【高校精品课-上海交通大学-企业级应用体系架构: Neo4J 2】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/75/detail/15851


Neo4J 2

 

目录

一、Neo4J

二、RocksDB

 

一、Neo4J

1.运行模式

一种嵌入式像 Derby、hs 数据库一样,可以和应用跑到同一进程里。服务器模式运行,可以在集群里跑,负载均衡器将请求按照循环赛、锦标赛、最近最少请求的方式做负载均衡。

把请求调回到不同节点,每一个服务器上都有缓存,访问数据库后,访问的数据放到缓存里。提高效率,每一台服务器都加载过数据,按照访问数据具体是哪部分,决定哪调动回请求。

image.png

MySQL 数据库做读写分离,在一个节点写,所有节点读。写操作在Msater 完成,将写操作同步到其它节点。

 image.png

2.内部存储模式

对节点和 bn 都做存储,节点存储有一个标志位,表示是否在使用。被删除,在标记上打一个标记,类似禁用,不会物理上删除。后面四位存储节点指向第一个关系 id,每个关系有一个 id。5-9指向下一个属性,节点有属性,属性是键值对,可能不只一个,记录第一个。1-5记录第一个关系,9-14上有一个 labels,是一个分类,最后是保留位。

image.png

关系,前面一个 inUse,1-5记住起点在哪,关系是有向边。5-9是终点,9-17关系类型存储。Node 只能记录某一类型,如果某一节点有多个关系存在,指向第一个关系,剩下关系靠链表,链起来。起点前一个关系是谁,后一个关系是谁。起点有很多关系,有一个链表指向下一个关系。前一个关系,后一个关系有双向的链表。21-25终点的前一个关系,25-29后一个关系,29-33关系属性中下一个属性是谁。

image.png

一个节点上有很多属性,指向第一个属性。每一个属性链接到下一个属性,可以访问到所有属性。Node1和Node2之间有一个 LIKES 关系,Node1有别的关系。LIKES指向起点的前一个关系,然后指向起点的后一个关系。Node2中 LIKES 指向终点的前一个关系和后一个关系,labels 都是 Person。简单的两个节点中间一个连接构成的图,简单的存储方式。

image.png

A、B、C、D、E五个节点,分别有自己的属性,用P1到P10表示,每个节点两个属性。互相之间有关系,R1到R7表示

image.png

存储结构,inUse 标志位,表示是否使用,结构初始状态是使用。下一个属性记录,A 第一个关系R1,只需记录R1,第一个属性 P1,记住 P1。B 第一个关系 R1,如果按序号表示前后关系,第一个属性为 P3。C 第一个关系是R2,第一个属性是 P5,以此类推,存储节点信息。

image.png

3.存储关系

R1关系,起点是 A,终点是 B。前一个关系是空,R1前面没有关系,按照序号表示关系的顺序。后面关系是R2,指针指向 R2。B前一个节点,前一个关系为空,后一个关系按照顺序是 R3,指针指向 R3。R2关系从A到C前一个关系是R1,后一个关系指向空。后一个关系 A 只有 R1和 R2,后面没有,所以指向空。C 是终点,终点在R之前没有,序号大的 R5,指向 R5。剩下以此类推,以此记录关系数据存储,双向列表提高存储效率,向前推,向后推都很快。关系存储图与节点存储合在一起是完整的存储方式,将所有的信息存储。

image.png

Neo4J 里用图本身绘制 Neo4J 本身的基础体系,有一个驱动,提高开源,需要驱动做连接,驱动支持多种语言。用 Java 开发,找 Java驱动。客户端包含浏览器类型,确定浏览器,协议呈现内容,对 Neo4J数据进行操作,包括开发工具、查询语言、可视化工具。可做集成,作为插件插入,比如插入到 idea 框架 IDE 里或其它框架里。拿图的方式描述体积架构。

image.png

做开发写 spring.boot 应用,用 Neo4J 的 data,包含自己写的应用定义。

<dependencies>

<dependency>

<groupld>org.springframework.boot</groupld>

<artifactld>spring-boot-starter-data-Neo4J</artifactld>

</dependency>

<dependency>

<groupld>org.springframework.boot</groupld>

<artifactld>spring-boot-starter-test</artifactld>

<scope>test</scope>

<exclusions>

<exclusion>

<groupld>org.junit.vintage</groupld>

<artifactld>junit-vintage-engine</artifactld>

</exclusion>

</exclusions>

</dependency>

</dependencies>

使用时以 person 为例,图中存的人包含 id、name。使用Neo4J,j使用 NodeEntity 标注。Id 表示主键。GeneratedValue 主键不是自己写的,Generated 写后读值。

无参数空构造器,Neo4J 强调必须有,删除报错。人里存储关系,一些人是队友,队友本身是人,teammates 是集合类型,里面的 person。Relationship 表示关系,Person 与其它 Person 的编关系。类型是 labels,相当于将不同关系的属性存储到一起。A 和 B是队友,B 和 A也是队友,方向无向,意味着是双向的。

worksWith 为了操作 Set,把一个人添加到teammates。getName、setName 把本身名字返回,toString 拿出队友信息。如果队友数据不为空,用map函数每一个人调用 getName,collect 函数把 name 组装到一个 list 里,list 与当前人的名字与前面字符串连接到一起。输出谁是队友是谁,输出此数据。

@NodeEntity

public class Person {

@id @GeneratedValue

private Long id

private String name;

private Person() {

// Empty constructor required as of Neo4J AP 2.0.5

};

public Person(String name){

this.name=name

}

@Relationship(type = "TEAMMATE", direction =Relationship.UNDIRE

CTED)

public Set<Person> teammates;

public void worksWith(Person person) {

if (teammates == null) {

teammates=new HashSet<>()

}

teammates.add(person);

}

public String toString(){

return this.name + "'s teammates => '

+OptionalofNullable(this.teammates).orElse(

Collections.emptySet()).stream()

.map(Person::getName)

.collect(Collectors.toList())}

}

public String getName() {

return name;

}

public void setName(String name){

this.name=name;

}

}

使用 Neo4J 时用 spring 框架,可用 CrudRepository 接口,spring帮助生成相应的时间类,写定义的方法。如果方法是 findBy 开头,后面跟 Person,类里的属性,不用给任何方法体和 application 标注。

Repository 知道怎样帮助生成方法,按照 Name 属性查找,自动生成 findByName。

package org.reins.Neo4Jsample

import org.springframeworkdatarepositoryCrudRepository;

public interface PersonRepository extends CrudRepository<Person,

Long>{

Person findByName(String name);

}

SpringBoot 主类里,第一个所有的 SpringBoot 工程里都加 application,第二个是通用的 CrudRepository,任何地方都看不出是 Neo4J,需要说清楚是 Enable Neo4J Repositories,按照 Neo4J 操作。

@SpringBootApplication

@Enable Neo4J Repositories

public class Neo4JsampleApplication{

所有的 spring 代码都使用

private final static Logger log=LoggerFactory.getLogger(Neo4Jsa

mpleApplication.class)

public static void main(String[] args) throws Exception{

SpringApplication.run(Neo4JsampleApplication.class,args);

}

Bean 拿出定义的 PersonRepository 对象,执行一系列操作,用 Bean 描述用 spring 托管的东西。跑demo,通过personRepository 删除现有的所有内容,创建三个 Person。创建好后组一个对 list,通过 Repository 把三个对象写入到 Neo4J 里。

@Bean

CommandLineRunner demo(PersonRepository personRepository){

return args -> {

personRepository.deleteAll();

Person greg=new Person("Greg");

Person roy=new Person("Roy");

Person craig=new Person("Craig");

List<Person> team=Arrays.asList(greg,roy,craig);

log.info("Before linking up with Neo4i.….");

team.stream().forEach(person->log.info("\t"+person.toString()));

personRepository.save(greg)

personRepository.save(roy);

personRepository.save(craig);

按名字查找,查第一个人 greg。查到后将另外两个添加到 greg。greg 与 roy 和 craig 都有 teammates 的关系。找人 roy,与 craig 有一个关系。forEach 循环对每个人进行操作,通过personRepositoryfindByName(persongetName)找到每个人后 toString。toString 将 greg 所有的队友打印出来。

greg=personRepository.findByName(greg.getName()); greg.worksWith(roy);

greg.worksWith(craig);

personRepository.save(greg);

roy =personRepository.findByName(roy.getName());

royworksWith(craig);

// We already know that roy works with greg

personRepository.save(roy);

// We already know craig works with roy and greg

log.info("Lookup each person by name….");

team.stream().forEach(person->log.info(

"\t"+personRepositoryfindByName(persongetName()).toString()));

};

运行系统,看到以下内容

image.png

一开始都为空,添加边是无向边,经过添加两者之间都有关系。开始 greg、roy、craig,经过操作每个人都是另外两个队友,产生此输出。

客户端跑起来,进入客户端如下

image.png

添加工程,添加图。创建本地图或远程图

image.png

可设置密码,起名字,点击创建。

image.png

效果如下

image.png

可以停止,停掉等于库停止。

访问报错,数据库没有。

image.png

启动

image.png

跑以来可以看到效果,三个人创建好后,一个人为空。

image.png

工程上面 Neo4J Browser 同性化操作界面,点击,进入到库里

image.png

点库可看到 Person3个,TEAMMATE 关系有四个。

image.png

点 Person 或 TEAMMATE 可看到格式化结果

image.png

有四个,因为代码写的问题。代码中 greg 进去有两个 worksWith,全部放入。

点 greg,拉开,可看里面的内容。

image.png

移开有属性,id 为2,name 是 greg。属性是 greg,labels 是person。三个表示 person

以上为整个以编码的方式访问 Neo4J 的过程

 

二、RocksDB

文档如下

https://rocksdb.org/

https://rocksdb.org.cn/doc.html

https://github.com/facebook/rocksdb/

https://github.com/facebook/rocksdb/wiki/RocksDB-Basics

有一类日志型的数据库,日志结构的数据库的解决。日志特点是随着时间不断的推移,有新的日志写入。

写的日志越久,读取的概率越小。调程序时,经常看刚出的日志。日志放入数据库,日志文件的特点是并非所有的数据被访问的频率或概率一样,有些数据是热点数据,有些不太被访问。按照时间变化,在时序上越久的数据,越不会被访问。越新的数据,访问频率越高,尤其是刚刚产生的数据。程序出 bug后,希望马上看数据出了什么问题。此类数据既要存下来,又要能支持按时序访问的效率。越近的访问速率越快,越远访问频率越小。

越远放到访问不快的地方,能够保证快的地方比较小。内存比硬盘快,硬盘考虑怎样组织,让访问上有差异。

RocksDB 中数据按照日志的方式存储,特点是日志只写入一次。日志写入后不会改,相当于历史记录。

如果改,应该重新写一条日志,替换掉原来的日志。如果写到数据库的是 Key and values,一个Key1000,对应的值是 xyz,一旦写入,不会做修改。如果修改,新生成 Key1000的记录,改为 xyy。

保证所有的操作,一旦存在记录,访问时直接访问到 xyy 上,再也不会访问到 xyz 上,形式上改掉xyz。

RocksDB 基本工作原理

image.png

数据来后写入到内存里,叫做 MemTable 的部分。MemTable 满后写入到内存另一个地方不可变的 MemTable。两者之间的差异,可理解为 MemTable 是一个缓存,一直向里面写,状态一直在变化,满了导入到不可变的 MemTable。

不可变的 MemTable 本身是满的状态,不能向里面插入东西,里面的内容不该。如果是读操作,在内存里读,速度很快。不可变的 MemTable 数据相当热,比 MemTable未写满的数据老一点。数据读 MemTable、不可变的 MemTable 都是在内存里发生的。如果将不可变的 MemTable 开的大一些,最新、最热的数据在内存访问的概率比较大,访问的速率非常快。MemTable 写满导出,MemTable 不断的写,不是不变的,不代表里面的内容发生变化。MemTable 像水缸一样,不断有新的水倒入。

MemTable、Immutable MemTable都满,写出Immutable MemTable,腾出空间将 MemTable 导入 Immutable MemTable。Immutable MemTable 向下写动作是 Compaction,把大家处理。Immutable MemTable 写出的在硬盘上分层的处理,在Level0是新数据,不可变内存落下来的数据。

落下来时在Level0层为了快,不做处理,直接搬下来。写入数据时有id,不一定是基于整数递增的,有可能是用户指定或其它人规则生成。可能造成Level0生成的数据Key,中间没有重叠,SST写到硬盘的小文件,每个小文件里最大的id和最小的id互相形成的范围,在Level0层的小文件里有重叠。

当查找50的数据时,MemTable、Immutable MemTable 中没有找到,到 Level0层中找。Level0中50可能在多个小文件中都有,都去搜,约定此文件的尺寸小一点。请求在 MemTable 中有立刻返回,找不到在 Immutable MemTable 中找,找到立刻返回。找不到去下一层找,脱离内存,到硬盘里找相对复杂。在 Level0层中找,必须三个里面都找,三个有重叠。三个里有可能包含,但不知道哪一个,都需要找。硬盘里的文件不能太大,尺寸较小。

Level0层数据数量多时,做 Compaction 合并到 Level1 层。Level1层做合并工作,此层不再具有重复的 Key 范围。尺寸较大,将上面小文件汇成大文件。每层设置阈值,每层尺寸是上层×10。

Level1层满后在向下,将相邻的合并。Level1层开始Key的范围不再重复。

当查找50的数据时,MemTable 找不到去 Immutable MemTable找,Immutable MemTable 中没有找到,到 Level0层中找,Level0找不到去Level1找。底下的范围没有重叠,可能只在一个文件中找。极端情况,数据非常老,在底层找到。在极值里不会扫描所有数据,Level1层开始只扫描一个表,每层扫一个表可达到数据。

假设50数据较老,沉到底下。对50做更改,MemTable 有一个50,满了导入到 Immutable MemTable,满后下来。请求找50,比如更改后的50落到Level0层。两个50,先到 MemTable,然后到Immutab

le MemTable 中找,找不到去Level0层找,找不到扫描多个SST文件,扫描找到50,不再继续找,返回。效果是第一个50永远返回不到,永远在第二个50返回,相当于做一次修改。保证所有最新的数据访问效率提高,首先在内存,其次在小的文件,层次高的上。只有非常老的文件,才会一层层查到老层级上,查询很多大文件。

50做修改落到Level0层,再对50做修改,落到Immutable MemTable,向下落时做合并替换掉。如果50落到Level0层,Level2层的50访问不到,但浪费空间。隔一段时间将数据库中数据重整,将再也访问不到的数据处理删除,减少对硬盘的占用。否则数据库只进不出没有删除,尺寸不断变化。整个修改操作不会很频繁,相比需要提高访问速度,不希望因为做一次 Compaction,导致系统的性能受影响。

数据库是日志型数据库,日志特点是新进来的数据最容易被访问。保证新进来的数据在比较快的地方被访问到,只有很老的数据回花很长的时间搜索。以分层的架构方式,解决数据访问效率问题,前提是所有数据访问概率对等。

MongoDB,如果有若干台服务器 shard 访问,保证所有 shard 存储的数据块 chunk 数量的差小于等于2,保证所有 shard 存储的 chunk数量大体平衡。存储10TB数据,10个shard,每个存1TB数据,每2台数据的差最多128兆,相当平衡。适用的场景是10TB的数据被访问的概率一样,所有数据在语义上相等。不存在新产生的数据经常被访问,老数据没人访问。

所有数据均等访问,语义上重要性一样,适用 MongoDB。认为所有数据访问效率不一样,明显按照时间的序列衰减,新来的数据被访问的频率非常高,随着时间延长,衰减频率非常快,被访问的概率非常低,场景适用于日志结构数据。

数据来后到 MemTable 读,然后到 Immutable MemTable 读,以此类推。硬盘上分层存储,一张张 SSTable 表,每张表里记录键范围。Test1 键范围是 abc 到 hello,在 Level0表里。

Test2表从 bbc到 world,如果认为按字母序,SSTable 中Test1和Test2有重叠。

image.png

落入到 Level1后没有重叠,做 Compaction动作,从内存落到Level0。有多少 Key,向下落到硬盘里,将内存中的东西写入到硬盘。Data Block 存储的数据,Index Block 数据索引,合起来是 SSTTable 在Level0。为追求速度快,上面内容向下放时,只做排序或直接向下搬,在 Immutable MemTable 里有序。每次用户插入 Key 无法限制,Key 范围可能重复。

image.png

Level0层向下将Level0和Level1层进行合并,Level0层满后向下与Level1层表合并。拿L和L+1层所有表做多路归并排序,解决所有Level1层之间表范围没有重叠,并且Level1层存储数据落到L+1层合适的位置。L如果有四个数据,合并时将四个数据插入L+1层合理的范围。

 image.png

SSTable 存储方案是行式存储,SSTable 存数据时,将数据分成数据块,数据怎样存储。源数据块,描述数据块组成、位置、包含怎样的字段。索引块,对数据建索引,索引不一定在主键上,可能在某一列做索引,可以包含多列,可指定列做,放到一起是整个 SSTable。

SSTable 按行式存储,记录1存完后,存记录2,以此类推。行式存储适用于OLTP连接数务处理,定位到一行,进行修改、删除等操作时非常快。

image.png

统计一列,比如订单的总价,比较麻烦。可做基于列式的改写,与阿里合作项目把 RocksDB 里的 SSTable 改为列式存储,支持 OLAP 列式操作。自适应,根据数据被访问的类型,被列式或行式读取的多,自动对数据存储做转换。当 OLTP 操作多时,基于行式,否则存储基于列式。根据访问效率做转换,之前基于行式现在基于列式比较多,可转换为基于列式。

系统里数据根据数据的类型和使用方式决定怎样存储,很多操作用图数据库容易实现,用关系型数据库要频繁做缴引操作。日志型数据库,可以是 Key value 存储,和 MongoDB 存在差异,数据使用方式不同,一个所有数据语义重要性一样,一个随着时间推演,语义重要性迅速衰减。

在系统开发不仅有 MongoDB 或 MySQL 能解决,包含各种数据库的组成,一个系统里可以四种数据库都有。

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
2月前
关于Neo4j的使用及其基本命令
关于Neo4j的使用及其基本命令
120 0
|
2月前
|
存储 NoSQL API
使用Py2neo进行Neo4j图数据库的增删改查操作
使用Py2neo进行Neo4j图数据库的增删改查操作
94 5
|
4月前
|
API 数据库 开发者
Python连接Neo4j工具比较 Neo4j Driver、py2neo
Python连接Neo4j工具比较 Neo4j Driver、py2neo
152 0
|
7月前
|
存储 NoSQL 关系型数据库
|
SQL NoSQL 算法
Neo4j极简教程
图数据库是NoSQL类数据库的一大典型代表,在国内图数据库属于新兴事物,其优异的复杂关系解决方案引起了国内众多大型互联网公司及IT开发者的关注,而Neo4j是目前图形化数据库中最为出色、最为成熟的产品。
749 0
Neo4j极简教程
|
机器学习/深度学习 SQL 数据库
NEO4J的入门和一些简单的操作
> 持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,[点击查看活动详情](https://juejin.cn/post/7147654075599978532 "https://juejin.cn/post/7147654075599978532") # 引言 今天我们继续学习NEO4J. # 创建 创建语句我们一般会使用create指令 我们首先在控制台上输入`neo4j.bat console`启动neo4j 然后打开你的浏览器,然后在浏览器地址栏中输入 `http://localhost:7687 - Neo4j Browser](http:
|
存储 NoSQL Java
Neo4j学习笔记(一) 安装配置
Neo4j学习笔记(一) 安装配置
608 0
|
SQL 存储 NoSQL
Neo4j学习笔记(二) 查询语言
Neo4j学习笔记(二) 查询语言
216 0
|
数据库管理
Neo4j一些命令
Neo4j的CREATE命令
88 0
Neo4j的一些命令
delete命令
106 0