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~4 bit 表示 next 的高4位;
- 第 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部分:
- key_id(24 bits) : 第1 ~24 bit , property 的key 的 id
- type( 4 bits ): 第25 ~28 bit , property 的 value 的类型,支持 string, Interger,Boolean, Float, Long,Double, Byte, Character,Short, array.
- 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 的成员函数, 它实现了不同类型的属性值的编码.
</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 的成员函数.
</pre> <div> private Collection allocateStringRecords( byte[] chars ) { return stringPropertyStore.allocateRecordsFromBytes( chars ); }
3.5.2.3 allocateRecordsFromBytes 函数
allocateRecordsFromBytes 函数是 AbstractDynamicStore .java 的成员函数.
</pre> <div> protected Collection allocateRecordsFromBytes( byte src[] ) { return allocateRecordsFromBytes( src, Collections.emptyList().iterator(), recordAllocator ); }
3.5.2.4 allocateRecordsFromBytes 函数
allocateRecordsFromBytes 函数是 AbstractDynamicStore .java 的成员函数.
</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 中实现的,下面是其代码片段。
</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)构成。
由 udpwork.com 聚合
|
评论: 0
|
要! 要! 即刻! Now!