3.5 Property 的存储

下面是neo4j graph db 中,Property数据存储对应的文件:

neostore.propertystore.db

neostore.propertystore.db.arrays

neostore.propertystore.db.arrays.id

neostore.propertystore.db.id

neostore.propertystore.db.index

neostore.propertystore.db.index.id

neostore.propertystore.db.index.keys

neostore.propertystore.db.index.keys.id

neostore.propertystore.db.strings

neostore.propertystore.db.strings.id

neo4j 中, Property 的存储是由 PropertyStore, ArrayPropertyStore, StringPropertyStore 和PropertyKeyTokenStore 4种类型的Store配合来完成的.

类PropertyStore对应的存储文件是neostore.propertystore.db, 相应的用来存储 string/array 类型属性值的文件分别是neostore.propertystore.db.strings (StringPropertyStore) 和 neostore.propertystore.db.arrays(ArrayPropertyStore). 其存储模型示意图如下:

其中PropertyStore是Property最主要的存储结构,当Property的Key-Value对的Value 是字符串或数组类型并且要求的存储空间比较大,在PropertyStore中保存不了,则会存在StringPropertyStore/ ArrayPropertyStore这样的DynamicStore 中。如果长度超过一个block ,则分block存储,并将其在StringPropertyStore/ ArrayPropertyStore中的第1个block 的 block_id 保存到 PropertyStore类型文件相应record 的PropertyBlock字段中。

PropertyKeyTokenStore和StringPropertyStore 配合用来存储Propery的Key部分。Propery的Key是编码的,key 的 id 保存在 PropertyKeyTokenStore (即 neostore.propertystore.db.index),key 的字符串名保存在对应的StringPropertyStore类型文件neostore.propertystore.db.index.keys 中。

ArrayPropertyStore的存储格式见< 3.3.2 DynamicStore 类型>,下面分别介绍一下PropertyStore和PropertyKeyTokenStore(PropertyKeyTokenStore)的文件存储格式。

3.5.1   PropertyStore类型的存储格式

neostore.propertystore.db文件存储格式示意图如下,整个文件是有一个 RECORD_SIZE=41 Bytes 的定长数组和一个字符串描述符“PropertyStore v0.A.2”(文件类型描述TYPE_DESCRIPTOR和 neo4j 的 ALL_STORES_VERSION构成)。访问时,可以通过 prop_id 作为数组的下标进行访问。

下面介绍一下 property record 中每个字段的含义:

  • highByte(1 Byte):第1字节,共分成2部分

/*

* [pppp,nnnn] previous, next high bits

*/

byte modifiers = buffer.get();

    1. 第1~4 bit 表示 next 的高4位;
    2. 第 5~8 bit表示 prev 的高4位
  • prev(4 Bytes) : Node或Relationship 的属性是通过双向链表方式组织的,prev 表示本属性在双向链表中的上一个属性的id。第2~5字节是prev property_id的 低32位. 加上highByte字节的第 5~8 bit作为高4位,构成一个完整的36位property_id。
  • next(4 Bytes) : next 表示本属性在双向链表中的下一个属性的id。第6~9字节是next property_id的 低32位. 加上highByte字节的第 1~4 bit作为高4位,构成一个完整的36位property_id。
  • payload:  payload 由block_header(8 Bytes)加3个property_block(8 Bytes)组成,共计 32 Bytes.  block_header 分成3部分:
    1. key_id(24 bits) : 第1 ~24 bit , property 的key 的 id
    2. type( 4 bits ):   第25 ~28 bit , property 的 value 的类型,支持 string, Interger,Boolean, Float, Long,Double, Byte, Character,Short, array.
    3. payload(36 bits): 第29 ~64 bit, 共计36bit;对于Interger, Boolean, Float, Byte, Character , Short 类型的值,直接保存在payload;对于long,如果36位可以表示,则直接保存在payload,如果不够,则保存到第1个PropertyBlock中;double 类型,保存到第1个PropertyBlock中;对于 array/string ,如果编码后在 block_header及3个PropertyBlock 能保存,则直接保存;否则,保存到ArrayDynamicStore/StringDynamicStore 中, payload 保存其在ArrayDynamicStore中的数组下表。

3.5.2   String 类型属性值的保存过程

下面的代码片段展示了neo4j 中,比较长的 String 类型属性值的保存处理过程,其是如何分成多个 DynamicBlock 来存储的。

3.5.2.1        encodeValue 函数

encodeValue 函数是 PropertySTore.java 的成员函数, 它实现了不同类型的属性值的编码.

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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
</pre>
<div>
public void encodeValue( PropertyBlock block, int keyId, Object value )
 
{
 
if ( value instanceof String )
 
{   // Try short string first, i.e. inlined in the property block
 
String string = (String) value;
 
if ( LongerShortString.encode( keyId, string, block, PropertyType.getPayloadSize() ) )
 
{
 
return;
 
}
 
// Fall back to dynamic string store
 
byte[] encodedString = encodeString( string );
 
Collection valueRecords = allocateStringRecords( encodedString );
 
setSingleBlockValue( block, keyId, PropertyType.STRING, first( valueRecords ).getId() );
 
for ( DynamicRecord valueRecord : valueRecords )
 
{
 
valueRecord.setType( PropertyType.STRING.intValue() );
 
block.addValueRecord( valueRecord );
 
}
 
}
 
else if ( value instanceof Integer )
 
{
 
setSingleBlockValue( block, keyId, PropertyType.INT, ((Integer) value).longValue() );
 
}
 
else if ( value instanceof Boolean )
 
{
 
setSingleBlockValue( block, keyId, PropertyType.BOOL, ((Boolean) value ? 1L : 0L) );
 
}
 
else if ( value instanceof Float )
 
{
 
setSingleBlockValue( block, keyId, PropertyType.FLOAT, Float.floatToRawIntBits( (Float) value ) );
 
}
 
else if ( value instanceof Long )
 
{
 
long keyAndType = keyId | (((long) PropertyType.LONG.intValue()) << 24);
 
if ( ShortArray.LONG.getRequiredBits( (Long) value ) <= 35 )
 
{   // We only need one block for this value, special layout compared to, say, an integer
 
block.setSingleBlock( keyAndType | (1L << 28) | ((Long) value << 29) );
 
}
 
else
 
{   // We need two blocks for this value
 
block.setValueBlocks( new long[]{keyAndType, (Long) value} );
 
}
 
}
 
else if ( value instanceof Double )
 
{
 
block.setValueBlocks( new long[]{
 
keyId | (((long) PropertyType.DOUBLE.intValue()) << 24),
 
Double.doubleToRawLongBits( (Double) value )} );
 
}
 
else if ( value instanceof Byte )
 
{
 
setSingleBlockValue( block, keyId, PropertyType.BYTE, ((Byte) value).longValue() );
 
}
 
else if ( value instanceof Character )
 
{
 
setSingleBlockValue( block, keyId, PropertyType.CHAR, (Character) value );
 
}
 
else if ( value instanceof Short )
 
{
 
setSingleBlockValue( block, keyId, PropertyType.SHORT, ((Short) value).longValue() );
 
}
 
else if ( value.getClass().isArray() )
 
{   // Try short array first, i.e. inlined in the property block
 
if ( ShortArray.encode( keyId, value, block, PropertyType.getPayloadSize() ) )
 
{
 
return;
 
}
 
// Fall back to dynamic array store
 
Collection arrayRecords = allocateArrayRecords( value );
 
setSingleBlockValue( block, keyId, PropertyType.ARRAY, first( arrayRecords ).getId() );
 
for ( DynamicRecord valueRecord : arrayRecords )
 
{
 
valueRecord.setType( PropertyType.ARRAY.intValue() );
 
block.addValueRecord( valueRecord );
 
}
 
}
 
else
 
{
 
throw new IllegalArgumentException( "Unknown property type on: " + value + ", " + value.getClass() );
 
}
 
}

3.5.2.2        allocateStringRecords 函数

allocateStringRecords 函数是 PropertySTore.java 的成员函数.

1
2
3
4
5
6
7
8
9
</pre>
<div>
private Collection allocateStringRecords( byte[] chars )
 
{
 
return stringPropertyStore.allocateRecordsFromBytes( chars );
 
}

3.5.2.3        allocateRecordsFromBytes 函数

allocateRecordsFromBytes 函数是 AbstractDynamicStore .java 的成员函数.

1
2
3
4
5
6
7
8
9
10
11
</pre>
<div>
protected Collection allocateRecordsFromBytes( byte src[] )
 
{
 
return allocateRecordsFromBytes( src, Collections.emptyList().iterator(),
 
recordAllocator );
 
}

3.5.2.4        allocateRecordsFromBytes 函数

allocateRecordsFromBytes 函数是 AbstractDynamicStore .java 的成员函数.

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
</pre>
<div>
public static Collection allocateRecordsFromBytes(
 
byte src[], Iterator recordsToUseFirst,
 
DynamicRecordAllocator dynamicRecordAllocator )
 
{
 
assert src != null : "Null src argument";
 
List recordList = new LinkedList<>();
 
DynamicRecord nextRecord = dynamicRecordAllocator.nextUsedRecordOrNew( recordsToUseFirst );
 
int srcOffset = 0;
 
int dataSize = dynamicRecordAllocator.dataSize();
 
do
 
{
 
DynamicRecord record = nextRecord;
 
record.setStartRecord( srcOffset == 0 );
 
if ( src.length - srcOffset > dataSize )
 
{
 
byte data[] = new byte[dataSize];
 
System.arraycopy( src, srcOffset, data, 0, dataSize );
 
record.setData( data );
 
nextRecord = dynamicRecordAllocator.nextUsedRecordOrNew( recordsToUseFirst );
 
record.setNextBlock( nextRecord.getId() );
 
srcOffset += dataSize;
 
}
 
else
 
{
 
byte data[] = new byte[src.length - srcOffset];
 
System.arraycopy( src, srcOffset, data, 0, data.length );
 
record.setData( data );
 
nextRecord = null;
 
record.setNextBlock( Record.NO_NEXT_BLOCK.intValue() );
 
}
 
recordList.add( record );
 
assert !record.isLight();
 
assert record.getData() != null;
 
}
 
while ( nextRecord != null );
 
return recordList;
 
}

3.5.3   ShortArray 类型属性值的保存过程

ShortArray.encode( keyId, value, block, PropertyType.getPayloadSize() ), 它是在 kernel/impl/nioneo/store/ShortArray.java 中实现的,下面是其代码片段。

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
</pre>
<div>
public static boolean encode( int keyId, Object array, PropertyBlock target, int payloadSizeInBytes )
 
{
 
/*
 
*  If the array is huge, we don't have to check anything else.
 
*  So do the length check first.
 
*/
 
int arrayLength = Array.getLength( array );
 
if ( arrayLength > 63 )/*because we only use 6 bits for length*/
 
{
 
return false;
 
}
 
ShortArray type = typeOf( array );
 
if ( type == null )
 
{
 
return false;
 
}
 
int requiredBits = type.calculateRequiredBitsForArray( array, arrayLength );
 
if ( !willFit( requiredBits, arrayLength, payloadSizeInBytes ) )
 
{
 
// Too big array
 
return false;
 
}
 
final int numberOfBytes = calculateNumberOfBlocksUsed( arrayLength, requiredBits ) * 8;
 
if ( Bits.requiredLongs( numberOfBytes ) > PropertyType.getPayloadSizeLongs() )
 
{
 
return false;
 
}
 
Bits result = Bits.bits( numberOfBytes );
 
// [][][    ,bbbb][bbll,llll][yyyy,tttt][kkkk,kkkk][kkkk,kkkk][kkkk,kkkk]
 
writeHeader( keyId, type, arrayLength, requiredBits, result );
 
type.writeAll( array, arrayLength, requiredBits, result );
 
target.setValueBlocks( result.getLongs() );
 
return true;
 
}
 
private static void writeHeader( int keyId, ShortArray type, int arrayLength, int requiredBits, Bits result )
 
{
 
result.put( keyId, 24 );
 
result.put( PropertyType.SHORT_ARRAY.intValue(), 4 );
 
result.put( type.type.intValue(), 4 );
 
result.put( arrayLength, 6 );
 
result.put( requiredBits, 6 );
 
}

3.5.4   PropertyKeyTokenStore的文件存储格式

类PropertyTypeTokenStore对应的存储文件名是neostore.propertystore.db.index,其对应的存储格式如上图所示: 是一个长度为 RECORD_SIZE=9Bytes 的 record 数组和和一个字符串“PropertyIndexStore v0.A.2”(文件类型描述TYPE_DESCRIPTOR和 neo4j 的 ALL_STORES_VERSION构成)。访问时,可以通过 token_id 作为数组的下标进行访问。

record 是由 in_use(1 Byte) ,prop_count(4 Bytes), name_id(4 Bytes)构成。

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

  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 底层存储结构分析(4)

    3.3.2   DynamicStore 类型 3.3.2.1        AbstractDynamicStore 的存储格式 neo4j 中对于字符串等变长值的保存策略是用一组定长的 block ...

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

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

  6. docker镜像与容器存储结构分析

    注意:转载请注明出处:http://www.programfish.com/blog/?p=9 Docker是一个开源的应用容器引擎,主要利用linux内核namespace实现沙盒隔离,用cgrou ...

  7. Percona 开始尝试基于Ceph做上层感知的分布式 MySQL 集群,使用 Ceph 提供的快照,备份和 HA 功能来解决分布式数据库的底层存储问题

    本文由 Ceph 中国社区 -QiYu 翻译 英文出处:Using Ceph with MySQL 欢迎加入CCTG Over the last year, the Ceph world drew m ...

  8. Redis内存存储结构分析

    1 Redis 内存存储结构 本文是基于 Redis-v2.2.4 版本进行分析. 1.1 Redis 内存存储总体结构 Redis 是支持多key-value数据库(表)的,并用 RedisDb 来 ...

  9. doubango(2)--底层协议栈结构分析

    tsip_stack_handle_t 实例 1.        tsip_stack_handle_t的创建 在底层,真正运转的协议栈结构式tsip_stack_handle_t的一个实例,它的创建 ...

随机推荐

  1. 06.GitHub实战系列~6.过滤器过滤掉的文件如何上传

    比如我想发布一个软件版本供普通人下载: Git库建立了VS的过滤规则后 dll 和 exe 是自动过滤的,这时候我们得加参数(看图)git add xxx -f

  2. Guava monitor

    Guava的com.google.util.concurrent类库提供了相对于jdk java.util.concurrent包更加方便实用的并发类,Monitor类就是其中一个.Monitor类在 ...

  3. Jmeter 使用Jmeter与Badboy进行压力测试

    1. 介绍 Badboy是一个录制请求的工具,这里用它来生成文件给JMeter用. JMeter是一个用java写的开源的性能测试工具,用于模拟在服务器.网络或者其他对象上附加高负载以测试他们提供服务 ...

  4. Linux 硬盘分区生效命令partprobe

    在Linux中使用fdisk命令进行分区时,有时会遇到"WARNING: Re-reading the partition table failed with error 16: Devic ...

  5. iptables详解

    Netfilter包含有三种表,三种表下共包含有五种链,链下面包含各种规则.即表包含若干链,链包含若干规则.  (一)三种表为:filter   nat  mangle 1.filter:处理与本机有 ...

  6. win10安装oracle 11g 报错 要求的结果: 5.0,5.1,5.2,6.0 6.1 之一 实际结果: 6.2

    Windows10下安装Oracle11G.10G,都会提示如下信息 正在检查操作系统要求... 要求的结果: 5.0,5.1,5.2,6.0 之一 实际结果: 6.1 检查完成.此次检查的总体结果为 ...

  7. Oracle逻辑备份与恢复

      1. 备份的类型 按照备份方式的不同,可以把备份分为两类: 1.1 逻辑备份:指通过逻辑导出对数据进行备份.将数据库中的用户对象导出到一个二进制文件中,逻辑备份使用导入导出工具:EXPDP/IMP ...

  8. [原创]用C++类实现单向链表的增删查和反转操作

    数据结构这东西,理解起来不算难,但是实现难度就不小了,虽然思路很清晰,但不知道从何下手还有语言的细节问题一直是阻碍初学者的主要障碍(比如我).今天用了一下午时间终于独立完成了链表操作. 找网上的代码, ...

  9. 2 GPS utility methods

    Methond 1 is to check whether the GPS is on: 1 2 3 4 5 public boolean isGPSIsOn() { LocationManager ...

  10. 【codevs1993】草地排水 最大流

    [codevs1993]草地排水 题目描述 Description 在农夫约翰的农场上,每逢下雨,Bessie最喜欢的三叶草地就积聚了一潭水.这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段 ...