blob: a482789257d1e74295379d864f65395451028d1c [file] [log] [blame] [view]
## IoTDB CSharp Client ByteBuffer实现介绍
在进行`RowRecords`以及`Tablet`的插入时,我们需要对多行RowRecordTablet进行序列化以进行发送。客户端中的序列化实现主要依赖于ByteBuffer完成。接下来我们介绍ByteBuffer的实现细节。本文包含如下几点内容:
- 序列化的协议
- C#与Java的大小端的差异
- ByteBuffer内存倍增算法
### 一、序列化协议
客户端向IoTDB服务器发送的序列化数据总体应该包含两个信息。
- 数据类型
- 数据本身
其中对于`字符串`的序列化时,我们需要再加入字符串的长度信息。即一个字符串的序列化完整结果为:
[类型][长度][数据内容]
接下来我们分别介绍`RowRecord``Tablet`的序列化方式
#### 1.1 RowRecord
我们对RowRecord进行序列化时,`伪代码`如下:
public byte[] value_to_bytes(List<TSDataType> data_types, List<string> values){
ByteBuffer buffer = new ByteBuffer(values.Count);
for(int i = 0;i < data_types.Count(); i++){
buffer.add_type((data_types[i]);
buffer.add_val(values[i]);
}
}
对于其序列化的结果格式如下:
[数据类型1][数据1][数据类型2][数据2]...[数据类型N][数据N]
其中数据类型为自定义的`Enum`变量,分别如下:
public enum TSDataType{BOOLEAN, INT32, INT64, FLOAT, DOUBLE, TEXT, NONE};
#### 1.2. Tablet序列化
使用`Tabelt`进行数据插入时有如下限制:
限制:Tablet中数据不能有空值
由于向 `IoTDB`服务器发送`Tablet`数据插入请求时会携带`行数` `列数`, `列数据类型`,所以`Tabelt`序列化时我们不需要加入数据类型信息。`Tablet``按照列进行序列化`,这是因为后端可以通过行数得知出当前列的元素个数,同时根据列类型来对数据进行解析。
## CSharp与Java序列化数据时的大小端差异
由于Java序列化默认大端协议,而CSharp序列化默认得到小端序列。所以我们在CSharp中序列化数据之后,需要对数据进行反转这样后端才可以正常解析。同时当我们从后端获取到序列化的结果时(如`SessionDataset`),我们也需要对获得的数据进行反转以解析内容。这其中特例便是字符串的序列化,CSharp中对字符串的序列化结果为大端序,所以序列化字符串或者接收到字符串序列化结果时,不需要反转序列结果。
## ByteBuffer内存倍增法
拥有数万行的Tablet的序列化结果可能有上百兆,为了能够高效的实现大`Tablet`的序列化,我们对ByteBuffer使用`内存倍增法`的策略来减少序列化过程中对于内存的申请和释放。即当当前的buffer的长度不足以放下序列化结果时,我们将当前buffer的内存`至少`扩增2倍。这极大的减少了内存的申请释放次数,加速了大Tablet的序列化速度。
private void extend_buffer(int space_need){
if(write_pos + space_need >= total_length){
total_length = max(space_need, total_length);
byte[] new_buffer = new byte[total_length * 2];
buffer.CopyTo(new_buffer, 0);
buffer = new_buffer;
total_length = 2 * total_length;
}
}
同时在序列化`Tablet`时,我们首先根据Tablet`行数``列数`以及每一列的数据类型估计当前`Tablet`序列化结果所需要的内存大小,并在初始化时进行内存的申请。这进一步的减少了内存的申请释放频率。
通过上述的策略,我们在一个有`20000`行的Tablet上进行测试时,序列化速度相比Naive数组长度动态生长实现算法具有约35倍的性能加速。