Graph database_neo4j 底层存储结构分析(4)
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)的更多相关文章
- Graph database_neo4j 底层存储结构分析(8)
3.8 示例1:neo4j_exam 下面看一个简单的例子,然后看一下几个主要的存储文件,有助于理解<3–neo4j存储结构>描述的neo4j 的存储格式. 3.8.1 neo4j ...
- Graph database_neo4j 底层存储结构分析(7)
3.7 Relationship 的存储 下面是neo4j graph db 中,Relationship数据存储对应的文件: neostore.relationshipgroupstore.db ...
- Graph database_neo4j 底层存储结构分析(6)
3.6 Node 数据存储 neo4j 中, Node 的存储是由 NodeStore 和 ArrayPropertyStore 2中类型配合来完成的. node 的label 内容是存在Array ...
- Graph database_neo4j 底层存储结构分析(5)
3.5 Property 的存储 下面是neo4j graph db 中,Property数据存储对应的文件: neostore.propertystore.db neostore.propertys ...
- Graph database_neo4j 底层存储结构分析(1)
1 neo4j 中节点和关系的物理存储模型 1.1 neo4j存储模型 The node records contain only a pointer to their first pr ...
- Graph database_neo4j 底层存储结构分析(3)
3.3 通用的Store 类型 3.3.1 id 类型 下面是 neo4j db 中,每种Store都有自己的ID文件(即后缀.id 文件),它们的格式都是一样的. [test00]$ls - ...
- Graph database_neo4j 底层存储结构分析(2)
3 neo4j存储结构 neo4j 中,主要有4类节点,属性,关系等文件是以数组作为核心存储结构:同时对节点,属性,关系等类型的每个数据项都会分配一个唯一的ID,在存储时以该ID 为数组的 ...
- Redis(一) 数据结构与底层存储 & 事务 & 持久化 & lua
参考文档:redis持久化:http://blog.csdn.net/freebird_lb/article/details/7778981 https://blog.csdn.net/jy69240 ...
- HBase底层存储原理
HBase底层存储原理——我靠,和cassandra本质上没有区别啊!都是kv 列存储,只是一个是p2p另一个是集中式而已! 首先HBase不同于一般的关系数据库, 它是一个适合于非结构化数据存储的数 ...
随机推荐
- mac os x查看端口命令
`netstat` 命令 a. `netstat -nat | grep <端口号>` 转自: http://my.oschina.net/foreverich/blog/402252
- tcp/ip程序
#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #i ...
- python动态获取对象的属性和方法
http://blog.csdn.net/kenkywu/article/details/6822220首先通过一个例子来看一下本文中可能用到的对象和相关概念.01 #coding: UTF- ...
- 如何破解excel宏的密码
http://zhidao.baidu.com/question/140107193.html 最近下载了一个excel模板,使用excel宏编的,但实际需要需更改一下,但是他设置了工作表密码保护,谁 ...
- MPlayer-ww 增加边看边剪切功能
解压到 D:\MPlayer-ww 运行 copy_font.bat 安装字体 LED_font.ttf 双击 MPlayer_ww_openWith.reg 添加右键播放功能 outformat.i ...
- Linux多台服务器之间的文件共享
由于项目有个图片上传和导入导出的模块,所以当项目通过集群方式部署的时候就要考虑文件共享问题. 文件共享要么就是通过统一的文件系统来管理,要么就是在系统之间做文件共享,前者扩展性比较好,可以随时随地加服 ...
- innobackupex err2
报错: [root@DB dbdata]# innobackupex --defaults-file=/etc/my.cnf --user=root --password=123 /data/dbda ...
- SQL如何在数据库间复制表
方法一: DB1 tb1 DB2 tb2 选择DB1 到表的列表那里选择tb1表 右键 所有任务 数据导出 下一步 选择你要导出的数据库DB1 下一步 选择你要导入的数据库DB2 下一步 选 ...
- 深入学习SQL的Limit语句
一.基本语法 SQL的limit语法的如以下形式 SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset 当省略offset的时候 ...
- VIM Tutorials----(updating)
This is my first English tutorial; I hope I can help you to learn VIM. Any question send email to me ...