En comparación con Protocol Buffers, PlainBuffer ofrece un mejor rendimiento en la serialización y resolución de objetos pequeños. Por esta razón, Tablestore utiliza PlainBuffer para formatear los datos.
Este documento está dirigido a desarrolladores que implementan un SDK personalizado de Tablestore o depuran la transmisión de datos sin procesar a nivel de protocolo. Los desarrolladores de aplicaciones que usan un SDK existente no necesitan comprender el funcionamiento interno de PlainBuffer.
Definición del formato
Un mensaje de PlainBuffer consta de un encabezado seguido de una o más filas. Cada fila contiene una sección de clave primaria y una sección de columnas de atributos; ambas pueden ser opcionales según la operación (consulte la gramática a continuación). Las etiquetas actúan como delimitadores de campos: cada etiqueta indica al analizador qué elemento sigue, lo que le permite avanzar por el flujo de bytes sin ambigüedades.
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 de las etiquetas
La mayoría de las etiquetas son de un solo byte y marcan el inicio de un campo; tag_header ocupa 4 bytes. El analizador lee la etiqueta, determina qué campo continúa, lee la cantidad adecuada de bytes y avanza a la siguiente etiqueta.
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 en 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 de la suma de verificación
Las sumas de verificación usan CRC8. El cálculo sigue esta lógica:
El nombre, el valor, el tipo y la marca de tiempo de cada celda contribuyen a su suma de verificación.
El marcador de eliminación de cada fila aporta un solo byte:
0x1si la fila tiene marcador de eliminación, o0x0en caso contrario.La suma de verificación de la fila se calcula al ejecutar CRC8 sobre las sumas de verificación individuales de las celdas, no sobre los datos sin procesar de estas.
Implementación en Java:
El código a continuación se extrajo de $tablestore-4.2.1-sources/com/alicloud/openservices/tablestore/core/protocol/PlainBufferCrc8.java. Para obtener más información, consulte Installation.
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;
}
Ejemplo
La fila a continuación tiene dos columnas de clave primaria y cuatro columnas de atributos.
-
Columnas de clave primaria:
[pk1:string:iampk]
[pk2:integer:100]
-
Columnas de atributos:
[column1:string:bad:1001]
[column2:integer:128:1002]
[column3:double:34.2:1003]
[column4:del_all_versions]
Codificación:
<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]
Lectura de la primera celda de clave primaria byte por byte:
|
Bytes |
Etiqueta/campo |
Significado |
|
|
|
Inicio de una celda |
|
|
|
Indica que sigue el nombre de la celda |
|
|
value_type |
|
|
|
value_len |
El nombre mide 3 bytes |
|
|
value_data |
Nombre de la celda: |
|
|
|
Indica que sigue el valor de la celda |
|
|
value_type |
|
|
|
value_len |
El valor mide 5 bytes |
|
|
value_data |
Valor de la celda: |
|
|
|
Suma de verificación del nombre, valor, tipo y marca de tiempo |
La segunda celda de clave primaria sigue la misma estructura, salvo que el tipo de valor es 0x0 (VT_INTEGER) y value_len es 8 (un entero de 64 bits).
Las celdas de atributos agregan un campo de marca de tiempo (0x7 = tag_cell_ts) después del valor. column4 carece de valor: la etiqueta 0x6 (tag_cell_op) con valor 1 la identifica como una operación de eliminación de todas las versiones.