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) 以优化压缩比