neo4j 底层存储结构分析(6)

  • Post author:
  • Post category:IT
  • Post comments:0评论

上一节:neo4j 底层存储结构分析(5)

下一节:neo4j 底层存储结构分析(7)

3.6  Node 数据存储

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

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

neostore.nodestore.db

neostore.nodestore.db.id

neostore.nodestore.db.labels

neostore.nodestore.db.labels.id

ArrayPropertyStore的存储格式见< 3.3.2 DynamicStore 类型>,下面介绍一下 NodeStore 的文件存储格式。

3.6.1   NodeStore的主文件存储格式

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

neo4j_文件存储格式_NodeStore--20140410



&lt;div&gt;

// in_use(byte)+next_rel_id(int)+next_prop_id(int)+labels(5)+extra(byte)

public static final int RECORD_SIZE = 15;


下面介绍一下 node record 中每个字段的含义:
  • n  inUse(1 Byte):第1字节,共分成3部分

// [pppp,rrrx]

// [    ,   x] in use bit

// [    ,rrr  ] higher bits for rel id

// [pppp,    ] higher bits for prop id

long inUseByte = buffer.get();

    1. 第1 bit 表示 record 是否在 use;
    2. 第2~4 bit 表示 node 的第1个 relationship_id 的 高3位;
    3. 第 5~8 bit表示 node 的第1个property_id 的 高4位
  • next_rel_id(4 Bytes) : 第2~5字节是node 的第1个 relationship_id 的 低32位. 加上inUse 字节的第 2~4 bit作为高3位,构成一个完整的35位relationship_id。
  • next_prop_id(4 Bytes) : 第6~9字节是node 的第1个 property_id 的 低32位. 加上inUse 字节的第 5~8 bit作为高4位,构成一个完整的36 位 property_id。
  • labels(5 Bytes) : 第10~14字节是node 的label field。
  • extra(1 Byte) : 第15字节是 extra , 目前只用到第 1 bit ,表示该node 是否 dense, 缺省的配置是 该 node 的 relationshiop 的数量超过 50 个,这表示是 dense.

3.3.2   NodeStore.java

neo4j 中与neostore.nodestore.db文件相对应的类是NodeStore,负责NodeRecord在neostore.nodestore.db文件中的读写。

下面看一下 NodeStore.java 中 getRecord 成员函数,可以帮助理解 Node Record 的存储格式。

<br />&lt;div&gt;<br /><br />private NodeRecord getRecord( long id, PersistenceWindow window,<br /><br />RecordLoad load  )<br /><br />{<br /><br />Buffer buffer = window.getOffsettedBuffer( id );<br /><br />// [    ,   x] in use bit<br /><br />// [    ,xxx ] higher bits for rel id<br /><br />// [xxxx,    ] higher bits for prop id<br /><br />long inUseByte = buffer.get();<br /><br />boolean inUse = (inUseByte &amp; 0x1) == Record.IN_USE.intValue();<br /><br />if ( !inUse )<br /><br />{<br /><br />switch ( load )<br /><br />{<br /><br />case NORMAL:<br /><br />throw new InvalidRecordException( "NodeRecord[" + id + "] not in use" );<br /><br />case CHECK:<br /><br />return null;<br /><br />case FORCE:<br /><br />break;<br /><br />}<br /><br />}<br /><br />long nextRel = buffer.getUnsignedInt();<br /><br />long nextProp = buffer.getUnsignedInt();<br /><br />long relModifier = (inUseByte &amp; 0xEL) &lt;&lt; 31;<br /><br />long propModifier = (inUseByte &amp; 0xF0L) &lt;&lt; 28;<br /><br />long lsbLabels = buffer.getUnsignedInt();<br /><br />long hsbLabels = buffer.get() &amp; 0xFF; // so that a negative byte won't fill the "extended" bits with ones.<br /><br />long labels = lsbLabels | (hsbLabels &lt;&lt; 32);<br /><br />byte extra = buffer.get();<br /><br />boolean dense = (extra &amp; 0x1) &gt; 0;<br /><br />NodeRecord nodeRecord = new NodeRecord( id, dense, longFromIntAndMod( nextRel, relModifier ),<br /><br />longFromIntAndMod( nextProp, propModifier ) );<br /><br />nodeRecord.setInUse( inUse );<br /><br />nodeRecord.setLabelField( labels, Collections.&lt;DynamicRecord&gt;emptyList() );<br /><br />return nodeRecord;<br /><br />}<br /><br />

发表回复