图数据库NEO4J
Neo4j主要通过构成图来存储数据,图中的数据包括节点、关系以及节点的属性和关系的属性,关系可以是双向的,也可以是只有单向的.
以下是它的一些特点 + 支持完整的ACID(原子性、一致性、隔离性和持久性) + 支持常数级时间复杂度的图遍历 + 支持查询的数据导出为JSON和XLS格式 + 支持通过浏览器图形化界面形式访问 + 可以通过多种语言进行访问管理(Java、Python、Ruby、PHP、C#、Js)
原生图处理 native processing
原生图处理:存在免索引邻接属性(Index Free Adjacency),提供快速高效的图遍历
使用免索引邻接的数据库引擎中的每个节点都会维护其对相邻节点的引用。因此每个节点都表现为其附近节点的微索引,这比使用全局索引代价小很多。这意味着查询时间与图的整体规模无关,它仅和所搜索图的数量成正比。
相反,一个非原生图数据库引擎使用(全局)索引连接各个节点。这些索引对每个遍历都添加一个间接层,因此会导致更大的计算成本。原生图处理的拥护者认为免索引邻接至关重要,因为它提供快速、高效的图遍历。
索引查找在小型网络中可以工作,但对于大图的查询代价太高。具有原生图处理能力的图数据库在查询是不是使用索引查找来扮演联系的角色,而是使用免索引邻接来确保高性能遍历的。
非原生图处理引擎使用索引进行节点间遍历
上图中要寻找Alice的朋友,我们必须要首先执行索引查找,成本为O(log n ) ,这对于偶尔或者浅层的查找来说是可以接受的,但当我们改变遍历的方向时,他的代价就变得非常昂贵起来,如果相对于寻找alice的朋友,就必须要执行多个索引来完成查找,每个节点所代表的人都有可能把Alice当作他的朋友,这使得成本很高,找到Alice的朋友代价是O(log n ) ,而找到和Alice交朋友的人的代价则是O(mlogn)。
索引查找在小型网络中还可以,但是在大图中的查询代价太高,具有原生图处理能力的图数据库在查询时不是使用索引查找的,而是使用免索引零连接来确保高性能的遍历的,下图为Neo4j使用关系而非索引实现快速遍历
在通用图数据库中,可以以极小的代价双向(从尾部到头部或者从头部到尾部)遍历关系,上图中寻找ALICE的朋友,直接向外寻找friend就可以。其遍历的成本为O(1),要寻和Alice交朋友的人,我们只需要所有指向ALICE的friend关系联系在一起即可,这样的成本是O(1).
原生图存储 native graph storage
免索引邻接(index-free adjacency) 是图数据库相比于传统的 mysql 的优势的核心 key,那么图数据库用什么结构去存储 index-free adjacency 是关键设计点。
架构上层是对外访问的 api,右边是事务管理,左边有 cache 等,下面我们看下 disk 上存储的结构:
neo4j 在磁盘上会分不同的 store file 存储
- neostore.nodestore.db:存储 node
- neostore.propertystore.db:存储属性
- neostore.relationshipstore.db:存储关系
一个重要的设计点是 store 中存储的 record 都是固定大小的,固定大小带来的好处是:因为每个 record 的大小固定,因此给定 id就能快速进行定位。
节点与关系的存储文件的物理结构图 上图第一个是 node record 的结构:
- 1byte:in-use flag,表明该 node 是否在使用
- 4byte:第一个 relation id(-1表示无)
- 4byte:第一个 property id(-1表示无)
- 5byte:label 信息(可能直接 inline 存储)
- 1byte:reversed
图中的节点和联系的存储文件都是固定大小的,每个记录长度为9字节,因此可以可以在O(1)的时间复杂度下计算位置.
节点(指向联系和属性的单向链表,neostore.nodestore.db):第一个字节,表示是否被使用的标志位,后面4个字节,代表关联到这个节点的第一个关系的ID,再接着的4个字符,代表第一个属性ID,后面紧接着的5个字符是代表当前节点的标签,指向该节点的标签存储,最后一个字符作为保留位.
联系(双向链表,neostore.relationshipstore.db):第一个字节,表示是否被使用的标志位,后面4个字节,代表起始节点的ID,再接着的4个字符,代表结束个节点的ID,然后是关系类型占用5个字节,然后依次接着是起始节点的上下联系和结束节点的上下节点,以及一个指示当前记录是否位于联系链的最前面.
同时还有属性存储(neostore.propertystore.db)也是固定大小,每个属性记录包括4个属性块(一个属性记录最多容纳4个属性)和指向属性链中下一个属性的ID. 属性记录包括属性类型和指向属性索引文件的指针(neostore.propertysotre.db.index). 同时属性记录中可以内联和动态存储,在属性值存储占用小时,会直接存储在属性记录中,对于大属性值,可以分别存储在动态字符存储(neostore.propertysotre.db.strings)和动态数组存储(neostore.propertysotre.db.arrays)中,由于动态记录同样由记录大小固定的记录链表组成,因此大字符串和大数组会占据多个动态记录.
节点存储文件用来存储节点的记录。每个用户级的图中创建的节点最终会终结于节点存储,其物理文件是"neostore.nodestore.db"。像大多数Neo4j存储文件一样,节点存储区是固定大小的记录存储,每个记录长度为9字节。通过大小固定的记录可以快速查询存储文件中的节点。 一个节点记录的第一个字节是“是否在使用”标志位。它告诉数据库该记录目前是被用于存储节点,还是可回收用于表示一个新的节点。接下来的4字节表示关联到该节点的第一个联系,随后4字节表示该节点的第一个属性的ID。标签的5字节指向该节点的标签存储(如果标签很少的话也可以内联到节点中)。最后的字节extra是标志保留位。这样一个标志是用来标识紧密连接节点的,而省下的空间为将来预留。节点记录是相当轻量级的:它真的只是几个指向联系和属性列表的指针。 相应的,联系被存储于联系存储文件中,物理文件是neostore.relationshipstore.db。像节点存储一样,联系存储区的记录的大小也是固定的。每个联系记录包含联系的起始点ID和结束节点ID、联系类型的指针(存储在联系类型存储区),起始节点和结束节点的上一个联系和下一个联系,以及一个指示当前记录是否位于联系链最前面。
下面是 relation record 的结构:
刚开始是开始和结束节点的 node id,接着是 relation type pointer,然后开始和结束节点的前驱和后继 relation id
更形象一点的图
一个可能的搜索过程是:对于给定的一个 node record,可以通过 id 进行简单的偏移计算得到 node,然后通过 relation_id 定位到 relation record,然后得到 end node id,通过偏移计算得到 node
两个节点记录都包含一个指向该节点的第一个属性的指针和联系链中第一个联系的指针。要读取节点的属性,我们从指向第一个属性的指针开始遍历单向链表结构。要找到一个节点的联系,我们从指向第一个联系(在示例中为LIKES联系)的节点联系指针开始,顺着特定节点的联系的双向链表寻找(即起始节点的双向链表或结束节点的双向链表),直到找到感兴趣的联系。一旦找到了我们想要的联系记录,我们可以使用和寻找节点属性一样的单向链表结构读取这种联系的属性(如果有的话),也可以使用联系关联的起始节点ID和结束节点ID检查它们的节点记录。用这些ID乘以节点记录的大小,就可以立即算出每个节点在节点存储文件中的偏移量。 联系存储文件中的双向链表:
双向存储
这种 partner 的关系天然就是双向的,但是我们存储的时候,难道要存储两个关系吗,如下图:
那肯定是不需要的,这种存储就是一种浪费,那到底 neo4j 中是怎么存储 partner 这种双向关系的呢? 答案是:以任意一个节点为开端,另一个为尾端,即存储成为单向的关系
在 neo4j 中任意的关系都有一个 start node 和一个 end node,而且 start node 和 end node 都会有个关联的双向链表,这个双向链表中就记录了从该节点出去和进入的所有关系
示例1
在这个例子中,A ~ E表示Node 的编号,R1~R7 表示 Relationship 编号,P1~P10 表示Property 的编号。
Node 的存储示例图如下,每个Node 保存了第1个Property 和 第1个Relationship:
关系的存储示意图如下:
从示意图可以看出,从 Node-B 开始,可以通过关系的 next 指针,遍历Node-B 的所有关系,然后可以到达与其有关系的第1层Nodes,在通过遍历第1层Nodes的关系,可以达到第2层Nodes,...
neo4j graph db的存储文件介绍
下载neo4j-community-3.5.6并安装,运行几个例子后 在安装目录的/data/databases/graph.db下
[root@localhost graph.db]# ll
总用量 596
drwxr-xr-x 2 root root 10 5月 27 10:32 index
-rw-r--r-- 1 root root 8192 5月 28 09:30 neostore
-rw-r--r-- 1 root root 1152 5月 28 09:30 neostore.counts.db.a
-rw-r--r-- 1 root root 1152 5月 28 09:26 neostore.counts.db.b
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.id
-rw-r--r-- 1 root root 49152 5月 28 09:30 neostore.labelscanstore.db
-rw-r--r-- 1 root root 8190 5月 27 16:59 neostore.labeltokenstore.db
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.labeltokenstore.db.id
-rw-r--r-- 1 root root 8192 5月 27 16:59 neostore.labeltokenstore.db.names
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.labeltokenstore.db.names.id
-rw-r--r-- 1 root root 8190 5月 28 09:30 neostore.nodestore.db
-rw-r--r-- 1 root root 297 5月 28 09:30 neostore.nodestore.db.id
-rw-r--r-- 1 root root 8192 5月 27 10:32 neostore.nodestore.db.labels
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.nodestore.db.labels.id
-rw-r--r-- 1 root root 48954 5月 28 09:30 neostore.propertystore.db
-rw-r--r-- 1 root root 73728 5月 28 09:30 neostore.propertystore.db.arrays
-rw-r--r-- 1 root root 137 5月 28 09:30 neostore.propertystore.db.arrays.id
-rw-r--r-- 1 root root 265 5月 28 09:30 neostore.propertystore.db.id
-rw-r--r-- 1 root root 8190 5月 27 16:59 neostore.propertystore.db.index
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.propertystore.db.index.id
-rw-r--r-- 1 root root 8192 5月 27 16:59 neostore.propertystore.db.index.keys
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.propertystore.db.index.keys.id
-rw-r--r-- 1 root root 16384 5月 28 09:30 neostore.propertystore.db.strings
-rw-r--r-- 1 root root 89 5月 28 09:30 neostore.propertystore.db.strings.id
-rw-r--r-- 1 root root 8192 5月 27 10:32 neostore.relationshipgroupstore.db
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.relationshipgroupstore.db.id
-rw-r--r-- 1 root root 32640 5月 28 09:30 neostore.relationshipstore.db
-rw-r--r-- 1 root root 273 5月 28 09:30 neostore.relationshipstore.db.id
-rw-r--r-- 1 root root 8190 5月 27 16:59 neostore.relationshiptypestore.db
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.relationshiptypestore.db.id
-rw-r--r-- 1 root root 8192 5月 27 16:59 neostore.relationshiptypestore.db.names
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.relationshiptypestore.db.names.id
-rw-r--r-- 1 root root 8192 5月 27 10:32 neostore.schemastore.db
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.schemastore.db.id
-rw-r--r-- 1 root root 229292 5月 28 09:30 neostore.transaction.db.0
存储 node 的文件
- 存储节点数据及其序列Id
- neostore.nodestore.db: 存储节点数组,数组的下标即是该节点的ID
- neostore.nodestore.db.id :存储最大的ID 及已经free的ID
- 存储节点label及其序列Id
- neostore.nodestore.db.labels :存储节点label数组数据,数组的下标即是该节点label的ID
- neostore.nodestore.db.labels.id
存储 relationship 的文件
- 存储关系数据及其序列Id
- neostore.relationshipstore.db 存储关系 record 数组数据
- neostore.relationshipstore.db.id
- 存储关系组数据及其序列Id
- neostore.relationshipgroupstore.db 存储关系 group数组数据
- neostore.relationshipgroupstore.db.id
- 存储关系类型及其序列Id
- neostore.relationshiptypestore.db 存储关系类型数组数据
- neostore.relationshiptypestore.db.id
- 存储关系类型的名称及其序列Id
- neostore.relationshiptypestore.db.names存储关系类型 token 数组数据
- neostore.relationshiptypestore.db.names.id
存储 label 的文件
- 存储label token数据及其序列Id
- neostore.labeltokenstore.db 存储lable token 数组数据
- neostore.labeltokenstore.db.id
- 存储label token名字数据及其序列Id
- neostore.labeltokenstore.db.names 存储 label token 的 names 数据
- neostore.labeltokenstore.db.names.id
存储 property 的文件
- 存储属性数据及其序列Id
- neostore.propertystore.db 存储 property 数据
- neostore.propertystore.db.id
- 存储属性数据中的数组类型数据及其序列Id
- neostore.propertystore.db.arrays 存储 property (key-value 结构)的Value值是数组的数据。
- neostore.propertystore.db.arrays.id
- 属性数据为长字符串类型的存储文件及其序列Id
- neostore.propertystore.db.strings 存储 property (key-value 结构)的Value值是字符串的数据。
- neostore.propertystore.db.strings.id
- 属性数据的索引数据文件及其序列Id
- neostore.propertystore.db.index 存储 property (key-value 结构)的key 的索引数据。
- neostore.propertystore.db.index.id
- 属性数据的键值数据存储文件及其序列Id
- neostore.propertystore.db.index.keys 存储 property (key-value 结构)的key 的字符串值。
- neostore.propertystore.db.index.keys.id
其他的文件
- 存储版本信息
- neostore
- neostore.id
- 存储 schema 数据
- neostore.schemastore.db
- neostore.schemastore.db.id
neo4j 中,主要有4类节点,属性,关系等文件是以数组作为核心存储结构;同时对节点,属性,关系等类型的每个数据项都会分配一个唯一的ID,在存储时以该ID 为数组的下标。这样,在访问时通过其ID作为下标,实现快速定位。所以在图遍历等操作时,可以实现 free-index。
CommonAbstractStore
CommonAbstractStore 是所有 Store 类的基类,下面的代码片段是 CommonAbstractStore 的成员变量,比较重要的是飘红的几个,特别是IdGenerator,每种Store 的实例都有自己的 id 分配管理器; StoreChannel 是负责Store文件的读写和定位;WindowsPool 是与Store Record相关的缓存,用来提升性能的。
public abstract class CommonAbstractStore<RECORD extends AbstractBaseRecord,HEADER extends StoreHeader>
implements RecordStore<RECORD>, AutoCloseable
{
static final String UNKNOWN_VERSION = "Unknown";
protected final Config configuration;
protected final PageCache pageCache;
protected final IdType idType;
protected final IdGeneratorFactory idGeneratorFactory;
protected final Log log;
protected final String storeVersion;
protected final RecordFormat<RECORD> recordFormat;
final File storageFile;
private final File idFile;
private final String typeDescriptor;
protected PagedFile pagedFile;
protected int recordSize;
private IdGenerator idGenerator;
private boolean storeOk = true;
private RuntimeException causeOfStoreNotOk;
private final StoreHeaderFormat<HEADER> storeHeaderFormat;
private HEADER storeHeader;
private final OpenOption[] openOptions;
neo4j 的db文件及对应的存储格式类型
文件名 文件存储格式
neostore.labeltokenstore.db LabelTokenStore(TokenStore)
neostore.labeltokenstore.db.id ID 类型
neostore.labeltokenstore.db.names StringPropertyStore (AbstractDynamicStore, NAME_STORE_BLOCK_SIZE = 30)
neostore.labeltokenstore.db.names.id ID 类型
neostore.nodestore.db NodeStore
neostore.nodestore.db.id ID 类型
neostore.nodestore.db.labels ArrayPropertyStore (AbstractDynamicStorelabel_block_size=60)
neostore.nodestore.db.labels.id ID 类型
neostore.propertystore.db PropertyStore
neostore.propertystore.db.arrays ArrayPropertyStore (AbstractDynamicStorearray_block_size=120)
neostore.propertystore.db.arrays.id ID 类型
neostore.propertystore.db.id ID 类型
neostore.propertystore.db.index PropertyIndexStore
neostore.propertystore.db.index.id ID 类型
neostore.propertystore.db.index.keys StringPropertyStore (AbstractDynamicStore, NAME_STORE_BLOCK_SIZE = 30)
neostore.propertystore.db.index.keys.id ID 类型
neostore.propertystore.db.strings StringPropertyStore (AbstractDynamicStorestring_block_size=120)
neostore.propertystore.db.strings.id ID 类型
neostore.relationshipgroupstore.db RelationshipGroupStore
neostore.relationshipgroupstore.db.id ID 类型
neostore.relationshipstore.db RelationshipStore
neostore.relationshipstore.db.id ID 类型
neostore.relationshiptypestore.db RelationshipTypeTokenStore(TokenStore)
neostore.relationshiptypestore.db.id ID 类型
neostore.relationshiptypestore.db.names StringPropertyStore (AbstractDynamicStore, NAME_STORE_BLOCK_SIZE = 30)
neostore.relationshiptypestore.db.names.id ID 类型
neostore.schemastore.db SchemaStore(AbstractDynamicStore, BLOCK_SIZE = 56)
neostore.schemastore.db.id ID 类型
通用的Store 类型
下面是 neo4j db 中,每种Store都有自己的ID文件(即后缀.id 文件),它们的格式都是一样的。
[root@localhost graph.db]# ll | grep .id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.labeltokenstore.db.id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.labeltokenstore.db.names.id
-rw-r--r-- 1 root root 297 5月 28 09:30 neostore.nodestore.db.id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.nodestore.db.labels.id
-rw-r--r-- 1 root root 137 5月 28 09:30 neostore.propertystore.db.arrays.id
-rw-r--r-- 1 root root 265 5月 28 09:30 neostore.propertystore.db.id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.propertystore.db.index.id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.propertystore.db.index.keys.id
-rw-r--r-- 1 root root 89 5月 28 09:30 neostore.propertystore.db.strings.id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.relationshipgroupstore.db.id
-rw-r--r-- 1 root root 273 5月 28 09:30 neostore.relationshipstore.db.id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.relationshiptypestore.db.id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.relationshiptypestore.db.names.id
-rw-r--r-- 1 root root 9 5月 28 09:30 neostore.schemastore.db.id
ID类型文件的存储格式
neo4j 中后缀为 ".id"的文件格式如上图所示,由文件头(9 Bytes)和 long类型 数组 2部分构成:
- sticky(1 byte) : if sticky the id generator wasn't closed properly so it has to berebuilt (go through the node, relationship, property, rel type etc files).
- nextFreeId(long) : 保存最大的ID,该值与对应类型的存储数组的数组大小相对应。
- reuseId(long):用来保存已经释放且可复用的ID值。通过复用ID ,可以减少资源数组的空洞,提高磁盘利用率。
IdGeneratorImpl.java
原先,每一种资源类型的ID 分配 neo4j 中是通过 IdGeneratorImpl 来实现的,其功能是负责ID管理分配和回收复用。对于节点,关系,属性等每一种资源类型,都可以生成一个IdGenerator 实例来负责其ID管理分配和回收复用。
当前版本(v3.5)有所变动,应该是将原 IdGeneratorImpl 拆成了 IdGeneratorImpl 和 IdContainer .
读取id 文件进行初始化
public IdContainer( FileSystemAbstraction fs, File file, int grabSize, boolean aggressiveReuse )
{
if ( grabSize < 1 )
{
throw new IllegalArgumentException( "Illegal grabSize: " + grabSize );
}
this.file = file;
this.fs = fs;
this.grabSize = grabSize;
this.aggressiveReuse = aggressiveReuse;
}
public boolean init()
{
boolean result = true;
try
{
if ( !fs.fileExists( file ) )
{
createEmptyIdFile( fs, file, 0, false );
result = false;
}
fileChannel = fs.open( file, OpenMode.READ_WRITE );
initialHighId = readAndValidateHeader();
markAsSticky();
this.freeIdKeeper = new FreeIdKeeper( new OffsetChannel( fileChannel, HEADER_SIZE ), grabSize, aggressiveReuse );
closed = false;
}
catch ( IOException e )
{
throw new UnderlyingStorageException( "Unable to init id file " + file, e );
}
return result;
}
释放id(freeId)
/*********
IdGeneratorImpl.java
**********/
public synchronized void freeId( long id )
{
idContainer.assertStillOpen();
if ( IdValidator.isReservedId( id ) )
{
return;
}
if ( id < 0 || id >= highId )
{
throw new IllegalArgumentException( "Illegal id[" + id + "], highId is " + highId );
}
idContainer.freeId( id );
}
/*********
IdContainer.java
**********/
public void freeId( long id )
{
freeIdKeeper.freeId( id );
}
/**********************
FreeIdKeeper.java
**********************/
public void freeId( long id )
{
freeIds.enqueue( id );
freeIdCount++;
if ( freeIds.size() >= batchSize )
{
long endPosition = flushFreeIds( ByteBuffer.allocate( batchSize * ID_ENTRY_SIZE ) );
if ( aggressiveMode )
{
stackPosition = endPosition;
}
}
}
申请id ( nextId)
当用户申请一个 id 时,IdGeneratorImpl 在分配时,有2种分配策略:"正常的分配策略" 和"激进分配策略"(aggressiveMode),可以根据配置进行选择。
/**
* Returns the next "free" id. If a defragged id exist it will be returned
* else the next free id that hasn't been used yet is returned. If no id
* exist the capacity is exceeded (all values <= max are taken) and a
* {@link UnderlyingStorageException} will be thrown.
*
* @return The next free id
* @throws UnderlyingStorageException
* If the capacity is exceeded
* @throws IllegalStateException if this id generator has been closed
*/
@Override
public synchronized long nextId()
{
assertStillOpen();
long nextDefragId = idContainer.getReusableId();
if ( nextDefragId != IdContainer.NO_RESULT )
{
return nextDefragId;
}
if ( IdValidator.isReservedId( highId ) )
{
highId++;
}
IdValidator.assertValidId( idType, highId, max );
return highId++;
}
/*********************
IdContainer.java
**********************/
/**
* @return next free id or {@link IdContainer#NO_RESULT} if not available
*/
public long getReusableId()
{
return freeIdKeeper.getId();
}
/***************************
FreeKeeper.java
****************************/
public long getId()
{
long result;
if ( freeIds.size() > 0 && aggressiveMode )
{
result = freeIds.dequeue();
freeIdCount--;
}
else
{
result = getIdFromDisk();
if ( result != NO_RESULT )
{
freeIdCount--;
}
}
return result;
}
private long getIdFromDisk()
{
if ( readFromDisk.isEmpty() )
{
readIdBatch();
}
if ( !readFromDisk.isEmpty() )
{
return readFromDisk.dequeue();
}
else
{
return NO_RESULT;
}
}
- 激进分配策略(aggressiveMode)
- freeIds (刚回收的ID Queue)中分配。
- getIdFromDisk()
- 正常的分配策略
- getIdFromDisk()
其中, getIdFromDisk() 从 readFromDisk 文件中读取已释放且可复用的 id. 没有已释放且可复用的 id了,则分配全新的id.
/*
* After this method returns, if there were any entries found, they are placed in the readFromDisk list.
*/
private void readIdBatch()
{
try
{
readIdBatch0();
}
catch ( IOException e )
{
throw new UnderlyingStorageException( "Failed reading free id batch", e );
}
}
private void readIdBatch0() throws IOException
{
if ( stackPosition == 0 )
{
return;
}
long startPosition = max( stackPosition - batchSize * ID_ENTRY_SIZE, 0 );
int bytesToRead = toIntExact( stackPosition - startPosition );
ByteBuffer readBuffer = ByteBuffer.allocate( bytesToRead );
channel.position( startPosition );
channel.readAll( readBuffer );
stackPosition = startPosition;
readBuffer.flip();
int idsRead = bytesToRead / ID_ENTRY_SIZE;
for ( int i = 0; i < idsRead; i++ )
{
long id = readBuffer.getLong();
readFromDisk.enqueue( id );
}
if ( aggressiveMode )
{
truncate( startPosition );
}
}
参考资料
- 图数据库的内部结构 (NEO4j).https://cloud.tencent.com/developer/article/1387673
- https://neo4j.com/blog/native-vs-non-native-graph-technology/
- https://graphaware.com/neo4j/2013/10/11/neo4j-bidirectional-relationships.html
- https://www.tuicool.com/articles/NFzmQn
- http://sunxiang0918.cn/2015/06/27/neo4j-底层存储结构分析/
- 还没有人评论,欢迎说说您的想法!