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. android srudio使用HttpClient

    最近学习Android网络编程,在AndroidStudio下无法使用HttpClient,在网上找到了答案在这里记下来: //官方原文Apache HTTP Client RemovalAndroi ...

  2. 合理利用gradle的占位符功能

    1.gradle中可以声明字符串或者其他变量,然后再buildType中使用buildConfigField 来往BuildConfig文件中插入一个字符类型的常量,如下 先声明 def umengD ...

  3. 在MAC OS X上如何启用crontab?

    project: blog target: how-to-enable-crontab-on-osx.md date: 2015-12-16 status: publish tags: - OS X ...

  4. [刘阳Java]_Java程序员的成长路线_第3讲

    按照Java从业人员的职位晋升来说,Java程序成长路线大致如下 Java程序员 JavaEE初级软件工程师 JavaEE中级软件工程师 JavaEE高级软件工程师 Java架构师 按照职业发展方向, ...

  5. BackgroundCheck – 根据图片亮度智能切换元素样式

    BackgroundCheck 是一个轻量的 JavaScript 库,能够根据元素后面的图片的亮度自动切换元素样式.例如在图片幻灯片功能中,根据图片亮度调整导航箭头的颜色,这样让图片和导航的颜色形成 ...

  6. C#Winform从页面获取数据,传入数据库

    环境: 1.SQLite数据库新建数据表,设置相应的字段.(其他数据库形式都相似,using相应数据库的包即可) 2.页面有两个textBox:textBox1.textBox2, 3.一个保存按钮: ...

  7. java基础知识回顾之java Thread类学习(三)--java线程实现常见的两种方式实现好处:

    总结:实现Runnable接口比继承Thread类更有优势: 1.因为java只能单继承,实现Runnable接口可以避免单继承的局限性 2.继承Thread类,多个线程不能处理或者共享同一个资源,但 ...

  8. C#基础练习(使用委托窗体传值)

    主界面: Form1中的代码: namespace _06委托练习_窗体传值 {     public partial class Form1 : Form     {         public ...

  9. Codeforces Round #115 A. Robot Bicorn Attack 暴力

    A. Robot Bicorn Attack Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/17 ...

  10. 基于粒子群算法求解求解TSP问题(JAVA)

    一.TSP问题 TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题.货郎担问题,是数学领域中著名问题之一.假设有一个旅行商人要拜访n个城市,他必须选 ...