Dibandingkan dengan Protocol Buffers, PlainBuffer menawarkan kinerja serialisasi dan resolusi yang lebih baik untuk objek kecil. Oleh karena itu, PlainBuffer digunakan untuk memformat data di Tablestore.
Dokumen ini ditujukan kepada pengembang yang mengimplementasikan SDK Tablestore kustom atau melakukan debugging transmisi data mentah pada level protokol. Pengembang aplikasi yang menggunakan SDK yang telah tersedia tidak perlu memahami detail internal PlainBuffer.
Definisi format
Pesan PlainBuffer terdiri dari header yang diikuti oleh satu atau beberapa baris. Setiap baris mencakup bagian kunci primer dan bagian kolom atribut—keduanya bersifat opsional tergantung pada operasi (lihat tata bahasa di bawah). Tag berfungsi sebagai pembatas bidang; setiap tag memberi tahu parser tentang data berikutnya sehingga parser dapat melintasi aliran byte tanpa ambiguitas.
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)
Nilai tag
Sebagian besar tag terdiri dari satu byte yang menandai awal suatu bidang; tag_header terdiri dari 4 byte. Parser membaca tag tersebut, menentukan bidang yang mengikutinya, membaca jumlah byte yang sesuai, lalu melanjutkan ke tag berikutnya.
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)
Nilai ValueType
Nilai yang valid untuk value_type dalam 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
Perhitungan checksum
Checksum menggunakan CRC8 dan dihitung berdasarkan logika berikut:
Nama, nilai, tipe, dan timestamp setiap sel berkontribusi terhadap checksum sel tersebut.
Penanda hapus setiap baris menyumbang satu byte:
0x1jika baris memiliki penanda hapus,0x0jika tidak.Checksum baris dihitung dengan menjalankan CRC8 terhadap checksum masing-masing sel—bukan terhadap data sel mentah.
Implementasi Java:
Kode berikut diekstrak dari $tablestore-4.2.1-sources/com/alicloud/openservices/tablestore/core/protocol/PlainBufferCrc8.java. Untuk informasi lebih lanjut, lihat Instalasi.
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;
}
Contoh
Baris berikut memiliki dua kolom kunci primer dan empat kolom atribut.
-
Kolom kunci primer:
[pk1:string:iampk]
[pk2:integer:100]
-
Kolom atribut:
[column1:string:bad:1001]
[column2:integer:128:1002]
[column3:double:34.2:1003]
[column4:del_all_versions]
Pengkodean:
<Posisi awal untuk header>[0x75]
<Posisi awal untuk kolom kunci primer>[0x1]
<Sel1>[0x3][0x4][0x3][3][pk1][0x5][0x3][5][iampk][_cell_checksum]
<Sel2>[0x3][0x4][0x3][3][pk2][0x5][0x0][8][100][_cell_checksum]
<Posisi awal untuk kolom atribut>[0x2]
<Sel1>[0x3][0x4][0x3][7][column1][0x5][0x3][3][bad][0x7][1001][_cell_checksum]
<Sel2>[0x3][0x4][0x3][7][column2][0x5][0x0][8][128][0x7][1002][_cell_checksum]
<Sel3>[0x3][0x4][0x3][7][column3][0x5][0x1][8][34.2][0x7][1003][_cell_checksum]
<Sel4>[0x3][0x4][0x3][7][column4][0x6][1][_cell_checksum]
[_row_check_sum]
Membaca byte demi byte sel kunci primer pertama:
|
Byte |
Tag/bidang |
Makna |
|
|
|
Awal sebuah sel |
|
|
|
Nama sel mengikuti |
|
|
value_type |
|
|
|
value_len |
Nama sepanjang 3 byte |
|
|
value_data |
Nama sel: |
|
|
|
Nilai sel mengikuti |
|
|
value_type |
|
|
|
value_len |
Nilai sepanjang 5 byte |
|
|
value_data |
Nilai sel: |
|
|
|
Checksum atas nama, nilai, tipe, dan timestamp |
Sel kunci primer kedua mengikuti struktur yang sama, kecuali tipe nilainya adalah 0x0 (VT_INTEGER) dan value_len-nya adalah 8 (integer 64-bit).
Sel atribut menambahkan bidang timestamp (0x7 = tag_cell_ts) setelah nilai. column4 tidak memiliki nilai—tag 0x6 (tag_cell_op) dengan nilai 1 menandainya sebagai operasi hapus-semua-versi.