跳转至

Parquet编码(Encodings)

基本编码(PLAIN = 0)

支持的数据类型:所有

这是必须支持的基本编码方式,旨在提供最简单的编码形式。数据按顺序存储,无任何额外优化。

基本编码在无法使用更高效编码方式时使用,数据存储格式如下: - BOOLEAN(布尔型):采用位打包(Bit Packed)存储,最低有效位(LSB)优先 - INT32(32位整数):4字节小端序(little-endian) - INT64(64位整数):8字节小端序 - INT96(96位整数,已弃用):12字节小端序 - FLOAT(浮点数):4字节IEEE小端序 - DOUBLE(双精度浮点数):8字节IEEE小端序 - BYTE_ARRAY(字节数组):前4字节存储长度(小端序),后面紧跟数组内容 - FIXED_LEN_BYTE_ARRAY(定长字节数组):直接存储字节内容

对于原生类型,数据存储采用小端序。浮点类型则按IEEE标准编码。

对于字节数组类型,编码格式为:4字节小端序存储长度 + 数组字节内容


字典编码(PLAIN_DICTIONARY = 2 和 RLE_DICTIONARY = 8)

字典编码方式会为某个列构建值的字典,并在每个列块(column chunk)内存储一个字典页(dictionary page)。实际存储的数据是该字典中的索引,并使用RLE/位打包混合编码进行压缩。当字典过大(无论是大小还是唯一值个数)时,会回退到基本编码(PLAIN)。

存储结构: - 字典页:使用PLAIN编码存储字典条目 - 数据页:首字节存储比特宽度(最大值32),后续数据使用RLE/位打包混合编码存储索引值

在Parquet 2.0规范中,不推荐使用 PLAIN_DICTIONARY,应优先使用 RLE_DICTIONARY


运行长度编码 / 位打包混合编码(RLE = 3)

该编码方式结合了位打包(bit-packing)运行长度编码(run-length encoding, RLE),能够高效存储重复值。

编码结构

rle-bit-packed-hybrid: <length> <encoded-data>
// length字段并不总是出现,详情见下表
length := 4字节小端序(unsigned int32)存储 <encoded-data> 的长度
encoded-data := <run>*
run := <bit-packed-run> | <rle-run>
bit-packed-run := <bit-packed-header> <bit-packed-values>
bit-packed-header := varint-encode(<bit-pack-scaled-run-len> << 1 | 1)
bit-pack-scaled-run-len := (bit-packed-run-len) / 8
bit-packed-values := *具体格式见下*
rle-run := <rle-header> <repeated-value>
rle-header := varint-encode( (rle-run-len) << 1)
repeated-value := 按bit-width对齐的值

位打包(bit-packing)示例

假设数据 0, 1, 2, 3, 4, 5, 6, 7 采用3位bit-width存储:

dec value: 0   1   2   3   4   5   6   7
bit value: 000 001 010 011 100 101 110 111
bit label: ABC DEF GHI JKL MNO PQR STU VWX

实际存储格式如下(3字节):

bit value: 10001000 11000110 11111010
bit label: HIDEFABC RMNOJKLG VWXSTUPQ

这种存储方式在小端机器上读取更高效,适用于32位和64位寄存器的位操作。

数据页是否包含 length 字段

+--------------+------------------------+-----------------+
| Page kind    | RLE-encoded data kind  | Prepend length? |
+--------------+------------------------+-----------------+
| Data page v1 | Definition levels      | Y               |
|              | Repetition levels      | Y               |
|              | Dictionary indices     | N               |
|              | Boolean values         | Y               |
+--------------+------------------------+-----------------+
| Data page v2 | Definition levels      | N               |
|              | Repetition levels      | N               |
|              | Dictionary indices     | N               |
|              | Boolean values         | Y               |
+--------------+------------------------+-----------------+

对于 数据页v1,字典索引无需 length,但其他情况需要。


增量编码(Delta Encoding)

整数增量编码(DELTA_BINARY_PACKED = 5)

支持类型:INT32, INT64

该编码方式基于"Decoding billions of integers per second through vectorization"二进制打包算法

采用增量编码(delta encoding)后,存储的是连续值的增量,而非原始值。格式如下:

<header> <blocks>

头部结构

<block size in values> <number of miniblocks in a block> <total value count> <first value>
  • 块大小(block size):128的倍数(ULEB128)
  • 子块数量(miniblock count):块大小的因子(ULEB128)
  • 值总数(total value count):ULEB128
  • 首个值(first value):Zigzag ULEB128

示例 假设数据 7, 5, 3, 1, 2, 3, 4, 5: - 计算增量:-2, -2, -2, 1, 1, 1, 1 - 取最小增量 -2,计算相对增量:0, 0, 0, 3, 3, 3, 3 - 存储格式:

header: 8, 1, 8, 7
block: -2, 2, 00000011111111b

字节数组编码

增量长度字节数组(DELTA_LENGTH_BYTE_ARRAY = 6)

支持类型:BYTE_ARRAY

该编码方式优先于 PLAIN 编码。其基本策略是先对所有字节数组的长度进行增量编码(DELTA_BINARY_PACKED),然后将字节数组数据依次拼接。

存储结构:

<Delta Encoded Lengths> <Byte Array Data>

示例:

"Hello", "World", "Foobar", "ABCDEF"
DeltaEncoding(5, 5, 6, 6) + "HelloWorldFoobarABCDEF"

增量字符串(DELTA_BYTE_ARRAY = 7)

支持类型:BYTE_ARRAY, FIXED_LEN_BYTE_ARRAY

采用前缀压缩(incremental encoding/front compression): - 首先对前缀长度进行 DELTA_BINARY_PACKED 编码 - 再对后缀进行 DELTA_LENGTH_BYTE_ARRAY 编码

示例:

"axis", "axle", "babble", "babyhood"
DeltaEncoding(0, 2, 0, 3) + DeltaEncoding(4, 2, 6, 5) + "axislebabbleyhood"

最佳实践

  • 整数/布尔类型:优先使用 RLE/位打包混合编码
  • 字符串/字节数组:优先使用 增量编码(DELTA_BYTE_ARRAY)
  • 高重复值:采用 字典编码(RLE_DICTIONARY)
  • 浮点数:使用 字节流分割(BYTE_STREAM_SPLIT) 以优化压缩比