Todos os produtos
Search
Central de documentação

Tablestore:PlainBuffer

Última atualização: Jul 03, 2026

O PlainBuffer oferece melhor desempenho que o Protocol Buffers na serialização e análise de objetos pequenos. Por isso, o Tablestore usa esse formato para estruturar dados.

Este documento destina-se a desenvolvedores que implementam um SDK personalizado do Tablestore ou depuram a transmissão de dados brutos no nível de protocolo. Quem utiliza um SDK existente não precisa conhecer os detalhes internos do PlainBuffer.

Definição do formato

Uma mensagem PlainBuffer consiste em um cabeçalho seguido por uma ou mais linhas. Cada linha contém uma seção de chave primária e outra de colunas de atributo. Ambas podem ser opcionais, dependendo da operação (consulte a gramática abaixo). As tags atuam como delimitadores de campo: cada uma indica ao parser o próximo elemento, permitindo que ele avance pelo fluxo de bytes sem ambiguidade.

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)
                

Valores das tags

A maioria das tags ocupa um único byte e marca o início de um campo; tag_header ocupa 4 bytes. O parser lê a tag, identifica o campo seguinte, consome a quantidade adequada de bytes e avança para a próxima 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)
            

Valores de ValueType

Valores válidos para value_type em formated_value:

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
            

Cálculo do checksum

Os checksums usam CRC8. O cálculo segue esta lógica:

  • Nome, valor, tipo e timestamp de cada célula compõem o checksum correspondente.

  • O marcador de exclusão de cada linha contribui com um único byte: 0x1 se houver marcador, ou 0x0 caso contrário.

  • O checksum da linha resulta da aplicação do CRC8 sobre os checksums individuais das células, não sobre os dados brutos.

Implementação em Java:

Nota

O código a seguir foi extraído de $tablestore-4.2.1-sources/com/alicloud/openservices/tablestore/core/protocol/PlainBufferCrc8.java. Para mais informações, consulte Instalação.

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;
}
            

Exemplo

A linha a seguir tem duas colunas de chave primária e quatro colunas de atributo.

  • Colunas de chave primária:

    • [pk1:string:iampk]

    • [pk2:integer:100]

  • Colunas de atributo:

    • [column1:string:bad:1001]

    • [column2:integer:128:1002]

    • [column3:double:34.2:1003]

    • [column4:exclua_all_versions]

Codificação:

<The start position for headers>[0x75]
<The start position for primary key columns>[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]
<The start position for attribute columns>[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]

Leitura da primeira célula de chave primária byte a byte:

Bytes

Tag/campo

Significado

0x3

tag_cell

Início de uma célula

0x4

tag_cell_name

Indica que o nome da célula vem a seguir

0x3

value_type

VT_STRING — o nome é uma string

3

value_len

O nome tem 3 bytes

pk1

value_data

Nome da célula: pk1

0x5

tag_cell_value

Indica que o valor da célula vem a seguir

0x3

value_type

VT_STRING — o valor é uma string

5

value_len

O valor tem 5 bytes

iampk

value_data

Valor da célula: iampk

_cell_checksum

tag_cell_checksum + CRC8

Checksum calculado sobre nome, valor, tipo e timestamp

A segunda célula de chave primária segue a mesma estrutura, mas com tipo de valor 0x0 (VT_INTEGER) e value_len igual a 8 (inteiro de 64 bits).

As células de atributo incluem um campo de timestamp (0x7 = tag_cell_ts) após o valor. A column4 não tem valor: a tag 0x6 (tag_cell_op) com valor 1 indica uma operação de exclusão de todas as versões.