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

3.7  Relationship 的存储

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

neostore.relationshipgroupstore.db

neostore.relationshipgroupstore.db.id

neostore.relationshipstore.db

neostore.relationshipstore.db.id

neostore.relationshiptypestore.db

neostore.relationshiptypestore.db.id

neostore.relationshiptypestore.db.names

neostore.relationshiptypestore.db.names.id

neo4j 中, Relationship 的存储是由 RelationshipStore , RelationshipGroupStore, RelationshipTypeTokenStore和StringPropertyStore 4种类型的Store配合来完成的. 其中RelationshipStore 是Relationship最主要的存储结构;当一个Node 的关系数达到一定的阀值时,才会对关系分组(group), RelationshipGroupStore 用来保存关系分组数据;RelationshipTypeTokenStore和StringPropertyStore 配合用来存储关系的类型。

关系的类型的字符串描述值是存在StringPropertyStore这样的DynamicStore 中,如果长度超过一个block ,则分block存储,并将其在StringPropertyStore中的第1个block 的 block_id 保存到 RelationshipTypeTokenStore类型文件相应record 的name_id字段中。

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

3.7.1   RelationshipTypeTokenStore的主文件存储格式

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

record 是有 1Byte的 in_use 和 4Bytes 的 name_id 构成。

3.7.2   RelationshipStore的文件存储格式

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


1

2

3

4

5

6

7

8

9

10

11

</pre>

<div>// record header size

// directed|in_use(byte)+first_node(int)+second_node(int)+rel_type(int)+

// first_prev_rel_id(int)+first_next_rel_id+second_prev_rel_id(int)+

// second_next_rel_id+next_prop_id(int)+first-in-chain-markers(1)

public static final int RECORD_SIZE = 34;</div>

<pre>

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

  • in_use(1 Byte) : 第 1 字节, 分成3部分.

// [pppp,nnnx]
// [    ,   x] in use flag
// [    ,nnn ] first node high order bits
// [pppp,    ] next prop high order bits
第1 bit 表示 record 是否在 use;
第2~4 bit 表示first_node的node_id的高3位;
第 5~8 bit表示 next_prop_id 的property_id 的 高4位

  • first_node(4 Bytes) : 第2~5字节是RelationShip的from_node 的node_id 的低32位. 加上inUse 字节的第 2~4 bit 作为高3位,构成一个完整的35位node_id。
  • second_node(4 Bytes) : 第6~9字节是RelationShip的to_node 的node_id 的低32位. 加上rel_type的第29~31 bit作为高3位,构成一个完整的35位node_id。
  • rel_type(4 Bytes) : 第 10~13 字节, 分成6部分;

// [ xxx,    ][    ,    ][    ,    ][    ,    ] second node high order bits,     0×70000000

// [    ,xxx ][    ,    ][    ,    ][    ,    ] first prev rel high order bits,  0xE000000// [    ,   x][xx  ,    ][    ,    ][    ,    ] first next rel high order bits,  0x1C00000// [    ,    ][  xx,x   ][    ,    ][    ,    ] second prev rel high order bits, 0×380000// [    ,    ][    , xxx][    ,    ][    ,    ] second next rel high order bits, 0×70000

// [    ,    ][    ,    ][xxxx,xxxx][xxxx,xxxx] type

    1. 第29~31 位是second_node 的node_id高3位;
    2. 第26~28 位是first_next_rel_id 的 relationship_id高3位;
    3. 第23~25 位是first_next_rel_id 的relationship_id高3位;
    4. 第20~22 位是second_prev_rel_id 的relationship_id高3位;
    5. 第17~19 位是second_next_rel_id 的relationship_id高3位;
    6. 第 1~16 位 表示 RelationShipType;
  • first_prev_rel_id(4 Bytes) : 第14~17字节是from_node 的排在本RelationShip 前面一个RelationShip的 relationship_id 的低32位. 加上rel_type的第 26~28 bit 作为高3位,构成一个完整的35位relationship_id。
  • first_next_rel_id(4 Bytes) : 第18~21字节是from_node 的排在本RelationShip 前面一个RelationShip的 relationship_id 的低32位. 加上rel_type的第 23~25 bit 作为高3位,构成一个完整的35位relationship_id。
  • second_prev_rel_id(4 Bytes) : 第22~25字节是from_node 的排在本RelationShip 前面一个RelationShip的 relationship_id 的低32位. 加上rel_type的第 20~22 bit 作为高3位,构成一个完整的35位relationship_id。
  • second_next_rel_id(4 Bytes) : 第26~29字节是from_node 的排在本RelationShip 前面一个RelationShip的 relationship_id 的低32位. 加上rel_type的第 17~19 bit 作为高3位,构成一个完整的35位relationship_id。
  • next_prop_id(4 Bytes) : 第30~33字节是本RelationShip第1个Property的property_id 的低32位. 加上in_use的第 5~8 bit 作为高3位,构成一个完整的36 位property_id。
  • first-in-chain-markers(1 Byte) : 目前只用了第1位和第2位,其作用笔者还没搞清楚。

3.7.2.1        RelationshipStore.java

与neostore.relationshipstore.db文件相对应的类是RelationshipStore,负责RelationshipRecord从neostore.relationshipstore.db文件的读写。下面看一下 neostore.relationshipstore.db 中 getRecord 成员函数,可以帮助理解 Relationship Record 的存储格式。


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

</pre>

<div>

private RelationshipRecord getRecord( long id, PersistenceWindow window,RecordLoad load )

{

Buffer buffer = window.getOffsettedBuffer( id );

// [    ,   x] in use flag

// [    ,xxx ] first node high order bits

// [xxxx,    ] next prop high order bits

long inUseByte = buffer.get();

boolean inUse = (inUseByte & 0x1) == Record.IN_USE.intValue();

if ( !inUse )

{

switch ( load )

{

case NORMAL:

throw new InvalidRecordException( "RelationshipRecord[" + id + "] not in use" );

case CHECK:

return null;

}

}

long firstNode = buffer.getUnsignedInt();

long firstNodeMod = (inUseByte & 0xEL) << 31;

long secondNode = buffer.getUnsignedInt();

// [ xxx,    ][    ,    ][    ,    ][    ,    ] second node high order bits,     0x70000000

// [    ,xxx ][    ,    ][    ,    ][    ,    ] first prev rel high order bits,  0xE000000

// [    ,   x][xx  ,    ][    ,    ][    ,    ] first next rel high order bits,  0x1C00000

// [    ,    ][  xx,x   ][    ,    ][    ,    ] second prev rel high order bits, 0x380000

// [    ,    ][    , xxx][    ,    ][    ,    ] second next rel high order bits, 0x70000

// [    ,    ][    ,    ][xxxx,xxxx][xxxx,xxxx] type

long typeInt = buffer.getInt();

long secondNodeMod = (typeInt & 0x70000000L) << 4;

int type = (int)(typeInt & 0xFFFF);

RelationshipRecord record = new RelationshipRecord( id,

longFromIntAndMod( firstNode, firstNodeMod ),

longFromIntAndMod( secondNode, secondNodeMod ), type );

record.setInUse( inUse );

long firstPrevRel = buffer.getUnsignedInt();

long firstPrevRelMod = (typeInt & 0xE000000L) << 7;

record.setFirstPrevRel( longFromIntAndMod( firstPrevRel, firstPrevRelMod ) );

long firstNextRel = buffer.getUnsignedInt();

long firstNextRelMod = (typeInt & 0x1C00000L) << 10;

record.setFirstNextRel( longFromIntAndMod( firstNextRel, firstNextRelMod ) );

long secondPrevRel = buffer.getUnsignedInt();

long secondPrevRelMod = (typeInt & 0x380000L) << 13;

record.setSecondPrevRel( longFromIntAndMod( secondPrevRel, secondPrevRelMod ) );

long secondNextRel = buffer.getUnsignedInt();

long secondNextRelMod = (typeInt & 0x70000L) << 16;

record.setSecondNextRel( longFromIntAndMod( secondNextRel, secondNextRelMod ) );

long nextProp = buffer.getUnsignedInt();

long nextPropMod = (inUseByte & 0xF0L) << 28;

byte extraByte = buffer.get();

record.setFirstInFirstChain( (extraByte & 0x1) != 0 );

record.setFirstInSecondChain( (extraByte & 0x2) != 0 );

record.setNextProp( longFromIntAndMod( nextProp, nextPropMod ) );

return record;

}

3.7.3   RelationshipGroupStore类型的存储格式

当Node的Relationship数量超过一个阀值时,neo4j 会对 Relationship 进行分组,以便提供性能。neo4j 中用来实现这一功能的类是 RelationshipGroupStore.

其对应的文件存储格式如下:

整个文件是有一个 RECORD_SIZE=20Bytes 的定长数组和一个字符串“RelationshipGroupStore v0.A.2”(文件类型描述TYPE_DESCRIPTOR和 neo4j 的 ALL_STORES_VERSION构成)。访问时,可以通过 id 作为数组的下标进行访问。数组下标为0的 record 前4 Bytes 保存Relationship分组的阀值。

RelationshipGroupStore 的record 的格式如下:

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

// [    ,   x] in use

// [    ,xxx ] high next id bits

// [ xxx,    ] high firstOut bits

long inUseByte = buffer.get();

    1. 第1 bit: 表示 record 是否在 use;
    2. 第2~4 bit: 表示 next 的高3位;
    3. 第 5~7 bit:表示 firstOut高3位
    4. 第8 bit:没有用。
  • highByte(1 Byte):第1字节,共分成4部分

// [    ,   x] in use

// [    ,xxx ] high next id bits

// [ xxx,    ] high firstOut bits

long inUseByte = buffer.get();

    1. 第1 bit:没有用;
    2. 第2~4 bit: 表示 firstIn 的高3位;
    3. 第 5~7 bit:表示 firstLoop高3位
    4. 第8 bit:没有用。
    • next :
    • firstOut
    • firstIn
    • firstLoop
时间: 2024-10-12 20:10:55

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

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

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 neo

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

3.6  Node 数据存储 neo4j 中, Node 的存储是由 NodeStore 和 ArrayPropertyStore 2中类型配合来完成的. node 的label 内容是存在ArrayPropertyStore这样的DynamicStore 中,如果长度超过一个block ,则分block存储,并将其在ArrayPropertyStore中的第1个block 的 block_id 保存到 NodeStore类型文件相应record 的labels字段中. 下面是neo4j gra

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

1       neo4j 中节点和关系的物理存储模型 1.1  neo4j存储模型 The node records contain only a pointer to their first property and their first relationship (in what is oftentermed the _relationship chain). From here, we can follow the (doubly) linked-list of relationshi

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

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

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 Abs

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

3       neo4j存储结构 neo4j 中,主要有4类节点,属性,关系等文件是以数组作为核心存储结构:同时对节点,属性,关系等类型的每个数据项都会分配一个唯一的ID,在存储时以该ID 为数组的下标.这样,在访问时通过其ID作为下标,实现快速定位.所以在图遍历等操作时,可以实现 free-index. 3.1  neo4j 的 store 部分类图 3.1.1   CommonAbstractStore.java CommonAbstractStore 是所有 Store 类的基类,下面的

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

3.3  通用的Store 类型 3.3.1    id 类型 下面是 neo4j db 中,每种Store都有自己的ID文件(即后缀.id 文件),它们的格式都是一样的. [test00]$ls -lh target/neo4j-test00.db/ |grep .id -rw-r–r–9 04-11 13:28 neostore.id -rw-r–r–9 04-11 13:28 neostore.labeltokenstore.db.id -rw-r–r–9 04-11 13:28 neos

Docker底层存储结构

Docker底层存储结构 由于aufs并未并入内核,故而目前只有Ubuntu系统上能够使用aufs作为docker的存储引擎,而其他系统上使用lvm thin provisioning(overlayfs是一个和aufs类似的union filesystem,未来有可能进入内核,但目前还没有:Lvm snapshot are useful for doing e.g. backup of a snapshot, but regress badly in performance when you

Protobuf底层存储原理

参考官网, 序列化原理 底层二进制存储 message Test1 { optional int32 a = 1; } 并设置为a=150,序列化到一个文件中,查看文件,得到下面的二进制: 08 96 01 从底层存储的二进制值看出,Protobuf为什么这么快,节省内存了吧. 有以上的结果是因为 varints 这个特殊的东东.它可以让已个int数据类型的存储根据值的大小而自动改变存储的字节数. varint 中的每个字节,除了最后一个字节,都有最重要的位集--这表示还会有更多的字节.每个字节