因为 Protocol Buffer 序列化和解析小对象的性能很差,所以 OTS 自定义了 PlainBuffer 数据格式用来表示行数据。
格式定义plainbuffer = tag_header row1 [row2] [row3]
row = ( pk [attr] | [pk] attr | pk attr ) [tag_delete_marker] row_checksum;
pk = tag_pk cell_1 [cell_2] [cell_3]
attr = tag_attr cell1 [cell_2] [cell_3]
cell = tag_cell cell_name [cell_value] [cell_op] [cell_ts] cell_checksum
cell_name = tag_cell_name formated_value
cell_value = tag_cell_value formated_value
cell_op = tag_cell_op cell_op_value
cell_ts = tag_cell_ts cell_ts_value
row_checksum = tag_row_checksum row_crc8
cell_checksum = tag_cell_checksum row_crc8
formated_value = value_type value_len value_data
value_type = int8
value_len = int32
cell_op_value = delete_all_version | delete_one_version
cell_ts_value = int64
delete_all_version = 0x01 (1byte)
delete_one_version = 0x03 (1byte)
Tag取值tag_header = 0x75 (4byte)
tag_pk = 0x01 (1byte)
tag_attr = 0x02 (1byte)
tag_cell = 0x03 (1byte)
tag_cell_name = 0x04 (1byte)
tag_cell_value = 0x05 (1byte)
tag_cell_op = 0x06 (1byte)
tag_cell_ts = 0x07 (1byte)
tag_delete_marker = 0x08 (1byte)
tag_row_checksum = 0x09 (1byte)
tag_cell_checksum = 0x0A (1byte)
ValueType 取值
formated_value 中 value_type 的取值如下:
VT_INTEGER = 0x0
VT_DOUBLE = 0x1
VT_BOOLEAN = 0x2
VT_STRING = 0x3
VT_NULL = 0x6
VT_BLOB = 0x7
VT_INF_MIN = 0x9
VT_INF_MAX = 0xa
VT_AUTO_INCREMENT = 0xb
计算 Checksum
计算 checksum 的基本逻辑是:
void GetChecksum(uint8_t* crc, const InplaceCell& cell)
{
Crc8(crc, cell.GetName());
Crc8(crc, cell.GetValue().GetInternalSlice());
Crc8(crc, cell.GetTimestamp());
Crc8(crc, cell.GetOpType());
}
void GetChecksum(uint8_t* crc, const InplaceRow& row)
{
const std::deque<InplaceCell>& pk = row.GetPrimaryKey();
for (size_t i = 0; i < pk.size(); i++) {
uint8_t* cellcrc;
*cellcrc = 0;
GetChecksum(cellcrc, pk);
Crc8(crc, *cellcrc);
}
for (int i = 0; i < row.GetCellCount(); i++) {
uint8_t* cellcrc;
*cellcrc = 0;
GetChecksum(cellcrc, row.GetCell(i));
Crc8(crc, *cellcrc);
}
uint8_t del = 0;
if (row.HasDeleteMarker()) {
del = 1;
}
Crc8(crc, del);
}
<Header开始>[0x75]
<主键列开始>[0x1]
<Cell1>[0x3][0x4][3][pk1][0x5][3][5][iampk]
<Cell2>[0x3][0x4][3][pk2][0x5][0][100]
<属性列开始>[0x2]
<Cell1>[0x3][0x4][7][column1][0x5][0x3][3][bad][0x7][1001]
<Cell2>[0x3][0x4][7][column2][0x5][0x0][128][0x7][1002]
<Cell3>[0x3][0x4][7][column3][0x5][0x1][34.2][0x7][1003]
<Cell4>[0x3][0x4][7][column4][0x5][0x6][1]
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
从您的描述中,可以看出PlainBuffer数据格式是专为优化OTS(表格存储Table Store)中的小对象序列化和解析性能而设计的。这种自定义格式通过精细控制每个字段的编码,以及高效地计算校验和,来提升处理速度和减少资源消耗。下面是对您提供的信息的一些补充说明和应用指导:
结构化编码:PlainBuffer通过定义严格的标签(Tag)系统,确保了数据的有序和易于解析。每个部分都有明确的开始和结束标记,便于快速定位和读取所需数据。
性能优化:针对小对象,通过精简编码方式减少额外开销,比如使用单字节或固定长度编码来表示类型和长度,从而提高序列化和反序列化的效率。
校验和机制:在每个cell级别和row级别计算校验和,确保数据传输的完整性。特别是只对cell的checksum进行row级别的CRC计算,避免了重复处理原始数据,提高了计算效率。
灵活的数据类型支持:通过ValueType定义,支持多种数据类型,包括整数、浮点数、布尔值、字符串、二进制大对象等,满足多样化数据存储需求。
编写解码器/编码器:根据上述格式定义,开发专门的编码器将业务数据转换为PlainBuffer格式,以及解码器将接收到的PlainBuffer数据还原为业务可识别的对象。这通常涉及位操作和循环遍历tag-header到具体的value_type等各个部分。
性能测试:在实际应用前,对比使用Protocol Buffers和其他序列化方案(如JSON、Avro等)与PlainBuffer在处理小对象时的性能差异,确保选择最合适的方案。
错误处理与验证:实现时要加入充分的错误处理逻辑,比如校验和不匹配时的异常处理,确保数据的一致性和可靠性。
利用现有工具或库:虽然您提到了C语言的实现思路,但也可以考虑是否有现成的库或工具可以简化开发工作,或者参考阿里云官方是否提供了相关的SDK或工具集来辅助处理PlainBuffer数据。
PlainBuffer设计旨在解决特定场景下的性能瓶颈,特别是在处理大量小对象数据时。理解其内部结构和编码规则对于有效利用这一格式至关重要。结合具体应用场景,合理设计数据模型,并利用好其提供的性能优化特性,可以显著提升系统的整体表现。