PlainBuffer

更新时间:
复制为 MD 格式

PlainBuffer 是表格存储自定义的行数据序列化格式,针对小对象的序列化和解析性能进行了优化。本文档面向需要实现自定义 Tablestore SDK或调试底层数据传输的开发者。

格式定义

PlainBuffer 消息由一个 header 和若干行数据组成。每行包含主键列和属性列两个部分(根据操作类型,两者均可选)。tag 作为字段分隔符,告知解析器接下来需要读取的内容类型,使解析器能够在字节流中无歧义地推进。

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 占 1 字节,tag_header 占 4 字节。解析器读取 tag 后确定接下来的字段类型,读取相应字节数,然后推进到下一个 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_valuevalue_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 使用 CRC8 算法,计算规则如下:

  • 每个 cell 的 name、value、type 和 timestamp 参与该 cell 的 checksum 计算。

  • 每行的 delete marker 贡献 1 字节:有 delete marker 时补 0x1,无则补 0x0

  • 行的 checksum 通过对各 cell 的 checksum 执行 CRC8 得出,不直接对原始 cell 数据做 CRC。

Java 实现:

说明

以下代码摘自

$tablestore-4.2.1-sources/com/alicloud/openservices/tablestore/core/protocol/PlainBufferCrc8.java

,具体参见

Java SDK

public static byte getChecksum(byte crc, PlainBufferCell cell) throws IOException {

    if (cell.hasCellName()) {
        crc = crc8(crc, cell.getNameRawData());
    }

    if (cell.hasCellValue()) {
        if (cell.isPk()) {
            crc = cell.getPkCellValue().getChecksum(crc);
        } else {
            crc = cell.getCellValue().getChecksum(crc);
        }
    }

    if (cell.hasCellTimestamp()) {
        crc = crc8(crc, cell.getCellTimestamp());
    }

    if (cell.hasCellType()) {
        crc = crc8(crc, cell.getCellType());
    }

    return crc;
}

public static byte getChecksum(byte crc, PlainBufferRow row) throws IOException {
    for (PlainBufferCell cell : row.getPrimaryKey()) {
        crc = crc8(crc, cell.getChecksum());
    }

    for (PlainBufferCell cell : row.getCells()) {
        crc = crc8(crc, cell.getChecksum());
    }

    byte del = 0;
    if (row.hasDeleteMarker()) {
        del = (byte)0x1;
    }
    crc = crc8(crc, del);

    return crc;
}

举例

示例行包含 2 列主键和 4 列属性列:

  • 主键列:

    • [pk1:string:iampk]

    • [pk2:integer:100]

  • 属性列:

    • [column1:string:bad:1001]

    • [column2:integer:128:1002]

    • [column3:double:34.2:1003]

    • [column4:del_all_versions]

编码结果:

<Header开始>[0x75]
<主键列开始>[0x1]
  <Cell1>[0x3][0x4][0x3][3][pk1][0x5][0x3][5][iampk][_cell_checksum]
  <Cell2>[0x3][0x4][0x3][3][pk2][0x5][0x0][8][100][_cell_checksum]
<属性列开始>[0x2]
  <Cell1>[0x3][0x4][0x3][7][column1][0x5][0x3][3][bad][0x7][1001][_cell_checksum]
  <Cell2>[0x3][0x4][0x3][7][column2][0x5][0x0][8][128][0x7][1002][_cell_checksum]
  <Cell3>[0x3][0x4][0x3][7][column3][0x5][0x1][8][34.2][0x7][1003][_cell_checksum]
  <Cell4>[0x3][0x4][0x3][7][column4][0x6][1][_cell_checksum]
[_row_check_sum]

以第一个主键 cell(pk1)为例,逐字节解析如下:

字节

Tag/字段

含义

0x3

tag_cell

cell 开始

0x4

tag_cell_name

接下来是 cell name

0x3

value_type

VT_STRING——name 为字符串类型

3

value_len

name 长度为 3 字节

pk1

value_data

cell name:pk1

0x5

tag_cell_value

接下来是 cell value

0x3

value_type

VT_STRING——value 为字符串类型

5

value_len

value 长度为 5 字节

iampk

value_data

cell value:iampk

_cell_checksum

tag_cell_checksum + CRC8

对 name、value、type 和 timestamp 做 checksum

第二个主键 cell(pk2)结构相同,差异在于 value_type 为 0x0VT_INTEGER),value_len 为 8(64 位整数)。

属性列 cell 在 value 之后多一个 timestamp 字段(0x7 = tag_cell_ts)。column4 没有 value,0x6tag_cell_op)后接值 1,表示删除所有版本操作。