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. mac os x查看端口命令

    `netstat` 命令 a. `netstat -nat | grep <端口号>` 转自: http://my.oschina.net/foreverich/blog/402252

  2. tcp/ip程序

    #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #i ...

  3. python动态获取对象的属性和方法

    http://blog.csdn.net/kenkywu/article/details/6822220首先通过一个例子来看一下本文中可能用到的对象和相关概念.01     #coding: UTF- ...

  4. 如何破解excel宏的密码

    http://zhidao.baidu.com/question/140107193.html 最近下载了一个excel模板,使用excel宏编的,但实际需要需更改一下,但是他设置了工作表密码保护,谁 ...

  5. MPlayer-ww 增加边看边剪切功能

    解压到 D:\MPlayer-ww 运行 copy_font.bat 安装字体 LED_font.ttf 双击 MPlayer_ww_openWith.reg 添加右键播放功能 outformat.i ...

  6. Linux多台服务器之间的文件共享

    由于项目有个图片上传和导入导出的模块,所以当项目通过集群方式部署的时候就要考虑文件共享问题. 文件共享要么就是通过统一的文件系统来管理,要么就是在系统之间做文件共享,前者扩展性比较好,可以随时随地加服 ...

  7. innobackupex err2

    报错: [root@DB dbdata]# innobackupex --defaults-file=/etc/my.cnf --user=root --password=123 /data/dbda ...

  8. SQL如何在数据库间复制表

    方法一: DB1  tb1 DB2  tb2 选择DB1 到表的列表那里选择tb1表 右键 所有任务 数据导出 下一步  选择你要导出的数据库DB1  下一步 选择你要导入的数据库DB2 下一步  选 ...

  9. 深入学习SQL的Limit语句

    一.基本语法 SQL的limit语法的如以下形式 SELECT * FROM table  LIMIT [offset,] rows | rows OFFSET offset 当省略offset的时候 ...

  10. VIM Tutorials----(updating)

    This is my first English tutorial; I hope I can help you to learn VIM. Any question send email to me ...