3.3.2   DynamicStore 类型

3.3.2.1        AbstractDynamicStore 的存储格式

neo4j 中对于字符串等变长值的保存策略是用一组定长的 block 来保存,block之间用单向链表链接。类 AbstractDynamicStore 实现了该功能,下面是其注释说明。

/**

* An abstract representation of a dynamic store. The difference between a

* normal AbstractStore and a AbstractDynamicStore is

* that the size of a record/entry can be dynamic.

* Instead of a fixed record this class uses blocks to store a record. If a

* record size is greater than the block size the record will use one or more

* blocks to store its data.

* A dynamic store don’t have a IdGenerator because the position of a

* record can’t be calculated just by knowing the id. Instead one should use a

* AbstractStore and store the start block of the record located in the

* dynamic store. Note: This class makes use of an id generator internally for

* managing free and non free blocks.

* Note, the first block of a dynamic store is reserved and contains information

* about the store.

*/

AbstractDynamicStore 类对应的存储文件格式如上图所示, 整个文件是有一个block_size=BLOCK_HEADER_SIZE(8Bytes)+block_content_size的定长数组和一个字符串“StringPropertyStore v0.A.2”或“ArrayPropertyStore v0.A.2”或“SchemaStore v0.A.2”(文件类型描述TYPE_DESCRIPTOR和 neo4j 的 ALL_STORES_VERSION构成)。访问时,可以通过 id 作为数组的下标进行访问。其中,文件的第1个 record 中前4 字节用来保存 block_size。文件的第2个 record开始保存实际的block数据,它由8个字节的block_header和定长的 block_content(可配置)构成. block_header 结构如下:

  • inUse(1 Byte):第1字节,共分成3部分

[x__ ,    ]  0: start record, 1: linked record

[   x,    ]  inUse

[    ,xxxx]  high next block bits

    • 第1~4 bit 表示next_block 的高4位
    • 第5 bit表示block 是否在 use;
    • 第8 bit 表示 block 是否是单向链表的第1个 block;0 表示第1个block, 1表示后续 block.
  • nr_of_bytes(3Bytes):本 block 中保存的数据的长度。
  • next_block(4Bytes): next_block 的低 4 个字节,加上 inUse 的第1~4 位,next_block 的实际长度共 36 bit。以数组方式存储的单向链表的指针,指向保存同一条数据的下一个 block 的id.

3.3.2.2        AbstractDynamicStore.java

下面看一下 AbstractDynamicStore.java 中 getRecord() 和readAndVerifyBlockSize() 成员函数,可以帮助理解 DynamicStore 的存储格式。

  • getRecord( long blockId, PersistenceWindow window, RecordLoad load )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
private DynamicRecord getRecord( long blockId, PersistenceWindow window, RecordLoad load )</pre>
<div>{
 
DynamicRecord record = new DynamicRecord( blockId );
 
Buffer buffer = window.getOffsettedBuffer( blockId );
 
/*
 
* First 4b
 
* [x   ,    ][    ,    ][    ,    ][    ,    ] 0: start record, 1: linked record
 
* [   x,    ][    ,    ][    ,    ][    ,    ] inUse
 
* [    ,xxxx][    ,    ][    ,    ][    ,    ] high next block bits
 
* [    ,    ][xxxx,xxxx][xxxx,xxxx][xxxx,xxxx] nr of bytes in the data field in this record
 
*
 
*/
 
long firstInteger = buffer.getUnsignedInt();
 
boolean isStartRecord = (firstInteger & 0x80000000) == 0;
 
long maskedInteger = firstInteger & ~0x80000000;
 
int highNibbleInMaskedInteger = (int) ( ( maskedInteger ) >> 28 );
 
boolean inUse = highNibbleInMaskedInteger == Record.IN_USE.intValue();
 
if ( !inUse && load != RecordLoad.FORCE )
 
{
 
throw new InvalidRecordException( "DynamicRecord Not in use, blockId[" + blockId + "]" );
 
}
 
int dataSize = getBlockSize() - BLOCK_HEADER_SIZE;
 
int nrOfBytes = (int) ( firstInteger & 0xFFFFFF );
 
/*
 
* Pointer to next block 4b (low bits of the pointer)
 
*/
 
long nextBlock = buffer.getUnsignedInt();
 
long nextModifier = ( firstInteger & 0xF000000L ) << 8;
 
long longNextBlock = longFromIntAndMod( nextBlock, nextModifier );
 
boolean readData = load != RecordLoad.CHECK;
 
if ( longNextBlock != Record.NO_NEXT_BLOCK.intValue()
 
&& nrOfBytes < dataSize || nrOfBytes > dataSize )
 
{
 
readData = false;
 
if ( load != RecordLoad.FORCE )
 
{
 
throw new InvalidRecordException( "Next block set[" + nextBlock
 
+ "] current block illegal size[" + nrOfBytes + "/" + dataSize + "]" );
 
}
 
}
 
record.setInUse( inUse );
 
record.setStartRecord( isStartRecord );
 
record.setLength( nrOfBytes );
 
record.setNextBlock( longNextBlock );
 
/*
 
* Data 'nrOfBytes' bytes
 
*/
 
if ( readData )
 
{
 
byte byteArrayElement[] = new byte[nrOfBytes];
 
buffer.get( byteArrayElement );
 
record.setData( byteArrayElement );
 
}
 
return record;
 
}
  • readAndVerifyBlockSize()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
</pre>
<div>protected void readAndVerifyBlockSize() throws IOException
 
{
 
ByteBuffer buffer = ByteBuffer.allocate( 4 );
 
getFileChannel().position( 0 );
 
getFileChannel().read( buffer );
 
buffer.flip();
 
blockSize = buffer.getInt();
 
if ( blockSize <= 0 )
 
{
 
throw new InvalidRecordException( "Illegal block size: " +
 
blockSize + " in " + getStorageFileName() );
 
}
 
}

3.3.2.3        类DynamicArrayStore, DynamicStringStore

类SchemaStore,DynamicArrayStore(ArrayPropertyStore), DynamicStringStore(StringPropertyStore)都是继承成自类AbstractDynamicStore,所以与类DynamicArrayStore, DynamicStringStore和 SchemaStore对应文件的存储格式,都是遵循AbstractDynamicStore的存储格式,除了block块的大小(block_size)不同外。

db 文件 存储类型 block_size
neostore.labeltokenstore.db.names StringPropertyStore NAME_STORE_BLOCK_SIZE=30
neostore.propertystore.db.index.keys StringPropertyStore NAME_STORE_BLOCK_SIZE=30
neostore.relationshiptypestore.db.names StringPropertyStore NAME_STORE_BLOCK_SIZE=30
neostore.propertystore.db.strings StringPropertyStore string_block_size=120
neostore.nodestore.db.labels ArrayPropertyStore label_block_size=60
neostore.propertystore.db.arrays ArrayPropertyStore array_block_size=120
neostore.schemastore.db SchemaStore BLOCK_SIZE=56

block_size 通过配置文件或缺省值来设置的,下面的代码片段展示了neostore.propertystore.db.strings 文件的创建过程及block_size 的大小如何传入。

1)        GraphDatabaseSettings.java

1
2
3
4
5
6
7
</pre>
<div>public static final Setting string_block_size = setting("string_block_size", INTEGER, "120",min(1));
 
public static final Setting array_block_size = setting("array_block_size", INTEGER, "120",min(1));
 
public static final Setting label_block_size = setting("label_block_size", INTEGER, "60",min(1));</div>
<pre>
      2)        StoreFactory.java的Configuration 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre>
<div>public static abstract class Configuration
 
{
 
public static final Setting string_block_size = GraphDatabaseSettings.string_block_size;
 
public static final Setting array_block_size = GraphDatabaseSettings.array_block_size;
 
public static final Setting label_block_size = GraphDatabaseSettings.label_block_size;
 
public static final Setting dense_node_threshold = GraphDatabaseSettings.dense_node_threshold;
 
}

3)        StoreFactory.java的createPropertyStore 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre>
<div>public void createPropertyStore( File fileName )
 
{
 
createEmptyStore( fileName, buildTypeDescriptorAndVersion( PropertyStore.TYPE_DESCRIPTOR ));
 
int stringStoreBlockSize = config.get( Configuration.string_block_size );
 
int arrayStoreBlockSize = config.get( Configuration.array_block_size )
 
createDynamicStringStore(new File( fileName.getPath() + STRINGS_PART), stringStoreBlockSize, IdType.STRING_BLOCK);
 
createPropertyKeyTokenStore( new File( fileName.getPath() + INDEX_PART ) );
 
createDynamicArrayStore( new File( fileName.getPath() + ARRAYS_PART ), arrayStoreBlockSize );
 
}

4)        StoreFactory.java的createDynamicStringStore函数

1
2
3
4
5
6
7
8
</pre>
<div>private void createDynamicStringStore( File fileName, int blockSize, IdType idType )
 
{
 
createEmptyDynamicStore(fileName, blockSize, DynamicStringStore.VERSION, idType);
 
}

5)        StoreFactory.java的createEmptyDynamicStore 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
</pre>
<div>
/**
 
* Creates a new empty store. A factory method returning an implementation
 
* should make use of this method to initialize an empty store. Block size
 
* must be greater than zero. Not that the first block will be marked as
 
* reserved (contains info about the block size). There will be an overhead
 
* for each block of <CODE>AbstractDynamicStore.BLOCK_HEADER_SIZE</CODE>bytes.
 
*/
 
public void createEmptyDynamicStore( File fileName, int baseBlockSize,
 
String typeAndVersionDescriptor, IdType idType)
 
{
 
int blockSize = baseBlockSize;
 
// sanity checks
 
 
blockSize += AbstractDynamicStore.BLOCK_HEADER_SIZE;
 
// write the header
 
try
 
{
 
FileChannel channel = fileSystemAbstraction.create(fileName);
 
int endHeaderSize = blockSize
 
+ UTF8.encode( typeAndVersionDescriptor ).length;
 
ByteBuffer buffer = ByteBuffer.allocate( endHeaderSize );
 
buffer.putInt( blockSize );
 
buffer.position( endHeaderSize - typeAndVersionDescriptor.length() );
 
buffer.put( UTF8.encode( typeAndVersionDescriptor ) ).flip();
 
channel.write( buffer );
 
channel.force( false );
 
channel.close();
 
}
 
catch ( IOException e )
 
{
 
throw new UnderlyingStorageException( "Unable to create store "
 
+ fileName, e );
 
}
 
idGeneratorFactory.create( fileSystemAbstraction, new File( fileName.getPath() + ".id"), 0 );
 
// TODO highestIdInUse = 0 works now, but not when slave can create store files.
 
IdGenerator idGenerator = idGeneratorFactory.open(fileSystemAbstraction,
 
new File( fileName.getPath() + ".id"),idType.getGrabSize(), idType, 0 );
 
idGenerator.nextId(); // reserve first for blockSize
 
idGenerator.close();
 
}

Graph database_neo4j 底层存储结构分析(4)的更多相关文章

  1. Graph database_neo4j 底层存储结构分析(8)

    3.8  示例1:neo4j_exam 下面看一个简单的例子,然后看一下几个主要的存储文件,有助于理解<3–neo4j存储结构>描述的neo4j 的存储格式. 3.8.1    neo4j ...

  2. Graph database_neo4j 底层存储结构分析(7)

    3.7  Relationship 的存储 下面是neo4j graph db 中,Relationship数据存储对应的文件: neostore.relationshipgroupstore.db ...

  3. Graph database_neo4j 底层存储结构分析(6)

    3.6  Node 数据存储 neo4j 中, Node 的存储是由 NodeStore 和 ArrayPropertyStore 2中类型配合来完成的. node 的label 内容是存在Array ...

  4. Graph database_neo4j 底层存储结构分析(5)

    3.5 Property 的存储 下面是neo4j graph db 中,Property数据存储对应的文件: neostore.propertystore.db neostore.propertys ...

  5. Graph database_neo4j 底层存储结构分析(1)

    1       neo4j 中节点和关系的物理存储模型 1.1  neo4j存储模型 The node records contain only a pointer to their first pr ...

  6. Graph database_neo4j 底层存储结构分析(3)

    3.3  通用的Store 类型 3.3.1    id 类型 下面是 neo4j db 中,每种Store都有自己的ID文件(即后缀.id 文件),它们的格式都是一样的. [test00]$ls - ...

  7. Graph database_neo4j 底层存储结构分析(2)

    3       neo4j存储结构 neo4j 中,主要有4类节点,属性,关系等文件是以数组作为核心存储结构:同时对节点,属性,关系等类型的每个数据项都会分配一个唯一的ID,在存储时以该ID 为数组的 ...

  8. Redis(一) 数据结构与底层存储 & 事务 & 持久化 & lua

    参考文档:redis持久化:http://blog.csdn.net/freebird_lb/article/details/7778981 https://blog.csdn.net/jy69240 ...

  9. HBase底层存储原理

    HBase底层存储原理——我靠,和cassandra本质上没有区别啊!都是kv 列存储,只是一个是p2p另一个是集中式而已! 首先HBase不同于一般的关系数据库, 它是一个适合于非结构化数据存储的数 ...

随机推荐

  1. Sublime Text 2 入门及技巧

    看了 Nettuts+ 对 Sublime Text 2 的介绍, 立刻就兴奋了,诚如作者 Jeffrey Way 所说:“<永远的毁灭公爵>都发布了,TextMate 2 还没发”,你还 ...

  2. ios7技巧:你需要掌握的19个iOS7使用技巧

    从右往左滑动屏幕,可看到信息收到的时间. 指南针应用还可以用作水平仪,滑动屏幕即可. 被苹果称作Spotlight的搜索功能有所改变.在屏幕中间向下滑动即可打开该项功能,你可以搜索文本.邮件.应用.歌 ...

  3. 【Python】 Django 怎么实现 联合主键?

    unique_together¶ Options.unique_together¶ Sets of field names that, taken together, must be unique: ...

  4. AtomicInteger源码分析——基于CAS的乐观锁实现

    AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...

  5. iOS CALayer动画中使用的3个tree

    在网上经常看到关于layer的tree的描述,不太理解,今天找到了官方文档,原文在Core Animation Programming Guide 中. Layer Trees Reflect Dif ...

  6. (转)Sql Server 对锁的初步认识

    一:到底都有哪些锁 学习锁之前,必须要知道锁大概有几种???通常情况下作为码农我们只需知道如下几个锁即可... 1.S(Share)锁 为了方便理解,我们可以直接这么认为,当在select的时候在表, ...

  7. Java for LeetCode 070 Climbing Stairs

    You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb ...

  8. Java性能优化权威指南-读书笔记(五)-JVM性能调优-吞吐量

    吞吐量是指,应用程序的TPS: 每秒多少次事务,QPS: 每秒多少次查询等性能指标. 吞吐量调优就是减少垃圾收集器消耗的CPU周期数,从而将更多的CPU周期用于执行应用程序. CMS吞吐调优 CMS包 ...

  9. 数码管的封装实验 --- verilog

    数码管的封装实验.显示使能信号置高才可以显示.对于小数点不用,故不显示. 数码管分为共阴数码管和共阳数码管,数码管不同,编码不同,下面是两种数码管显示0-F以及消隐的不同编码: 共阴数码管(高有效): ...

  10. Java内存访问重排序笔记

    >>关于重排序 重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段. 重排序分为两类:编译期重排序和运行期重排序,分别对应编译时和运行时环境. > ...