Parquet 编码学习笔记

目录

一. 官方Parquet 编码文档

二. Plain Encoding

三. 字典编码

四. RLE / Bit-Packed 混合

4.1 编码格式

4.2 其中的 bit-packing 原理

4.3. varint-encode()

4.4 其它


一. 官方Parquet 编码文档

更详细的可以直接看原文。 本文只是一些个人的理解和学习笔记。

parquet-format/Encodings.md at master · apache/parquet-format · GitHubApache Parquet. Contribute to apache/parquet-format development by creating an account on GitHub.https://github.com/apache/parquet-format/blob/master/Encodings.md

目前貌似真实的 Parquet reader/ writer 只会处理 Plain Encoding,字典编码 和 RLE / BitPack 混合编码。

二. Plain Encoding

Plain: (PLAIN = 0)

这是类型必须支持的普通编码。 它旨在成为最简单的编码。 值被背对背编码。

每当无法使用更有效的编码时,就会使用普通编码。 它以以下格式存储数据:

BOOLEAN: Bit Packed, LSB first

INT32: 4 bytes little endian

INT64: 8 bytes little endian

INT96: 12 bytes little endian (deprecated)

FLOAT: 4 bytes IEEE little endian

DOUBLE: 8 bytes IEEE little endian

BYTE_ARRAY: length in 4 bytes little endian followed by the bytes contained in the array

FIXED_LEN_BYTE_ARRAY: the bytes contained in the array

For native types, this outputs the data as little endian. Floating point types are encoded in IEEE.

For the byte array type, it encodes the length as a 4 byte little endian, followed by the bytes.

三. 字典编码

字典编码会为一个给定的列建立一个字典。字典页面使用字典序 plain encoding 编码,数据页面使用 RLE/ Bit-Packed 混合模式编码。如果字典变得太大,无论是大小还是不同值的数量,编码都将回退到 plain encoding 编码。 首先写入字典页面,然后是列块的数据页面。

四. RLE / Bit-Packed 混合

rle-bit-packed-hybrid 是一种 rle 和 bitpacked 两种编码的混合模式。

4.1 编码格式

rle-bit-packed-hybrid: <length> <encoded-data>
length := length of the <encoded-data> in bytes stored as 4 bytes little endian (unsigned int32)
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)
// we always bit-pack a multiple of 8 values at a time, so we only store the number of values / 8
bit-pack-scaled-run-len := (bit-packed-run-len) / 8
bit-packed-run-len := *see 3 below*
bit-packed-values := *see 1 below*
rle-run := <rle-header> <repeated-value>
rle-header := varint-encode( (rle-run-len) << 1)
rle-run-len := *see 3 below*
repeated-value := value that is repeated, using a fixed-width of round-up-to-next-byte(bit-width)

length: 存储了编码数据的长度,unsigned int32,小端(小端表示法: 低位字节存放在低地址),4 字节。

编码数据有可能是使用 bit-packed,也有可能是使用 rle 的。

bit-packed-run 由 bit-packed-header 和 bit-packed-values 组成。其中 bit-packed-header 是通过 varint-encode 的方式存放个数的,而 bit-packed-values 存放使用 bit-packed 编码的数据。

rle-run 由 rle-header 和 repeated-value 组成。其中 rle-header 是通过 varint-encode 的方式存放个数的,而 repeated-value 存放使用 rle 重复的数据。

4.2 其中的 bit-packing 原理

这些值从每个字节的最低有效位到最高有效位打包,但每个值中的位顺序仍然是最高有效到最低有效的通常顺序。

比如从数字 1 到 7 可以使用 3 位来编码。

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

将编码成 

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

这种打包顺序的原因是在一次反序列化一个以上字节时,little-endian(小端表示法: 低位字节存放在低地址) 硬件上的字边界较少。 这是因为可以将 4 个字节读入 32 位寄存器(或 8 个字节读入 64 位寄存器),并且只需通过使用掩码进行移位和 OR 运算就可以解压缩值。 (要在大端机器上进行此优化,您必须使用已弃用的位打包编码中使用的排序) 

如果是使用 8 位宽进行编码如下,代码如下。

        public final void unpack8Values(ByteBuffer in, int inPos, int[] out, int outPos) {
            out[0 + outPos] = in.get(0 + inPos) & 255;
            out[1 + outPos] = in.get(1 + inPos) & 255;
            out[2 + outPos] = in.get(2 + inPos) & 255;
            out[3 + outPos] = in.get(3 + inPos) & 255;
            out[4 + outPos] = in.get(4 + inPos) & 255;
            out[5 + outPos] = in.get(5 + inPos) & 255;
            out[6 + outPos] = in.get(6 + inPos) & 255;
            out[7 + outPos] = in.get(7 + inPos) & 255;
        }

4.3. varint-encode()

varint-encode() is ULEB-128 encoding, see https://en.wikipedia.org/wiki/LEB128

要使用无符号 LEB128 (ULEB128) 对无符号数进行编码,首先要以二进制形式表示该数。 然后扩展零将数字扩展到 7 位的倍数(这样,如果数字非零,则最高有效的 7 位不全是 0)。 将数字分成 7 位一组。 为每个 7 位组输出一个编码字节,从最低有效到最高有效组。 每个字节将在其 7 个最低有效位中包含该组。 在除最后一个字节之外的每个字节上设置最高有效位。 数字零被编码为单个字节 0x00。

例如,以下是无符号数 624485 的编码方式:

MSB ------------------ LSB
      10011000011101100101  In raw binary 624485的二进制表示
     010011000011101100101  Padded to a multiple of 7 bits 补0到7位的倍数,这边是21位。
 0100110  0001110  1100101  Split into 7-bit groups 7-bits 一组,分成3组
00100110 10001110 11100101  Add high 1 bits on all but last (most significant) group to form bytes
                            在除最后一个字节之外(MSB)的每个字节上设置最高有效位。
    0x26     0x8E     0xE5  In hexadecimal 每个字节的16机制表示。

→ 0xE5 0x8E 0x26            Output stream (LSB to MSB)

4.4 其它

bit-packed-run-len 和 rle-run-len 必须在 [1, 2 的 31 次方 - 1] 范围内。 这意味着 Parquet 实现始终可以将运行长度存储在一个有符号的 32 位整数中。 这个长度限制不是 Parquet 2.5.0 和更早规范的一部分,但是最常见的 Parquet 实现无法读取更长的大小,因此在实践中,Parquet 编写者发出的内容是不安全的。

请注意,RLE 编码方法仅支持以下类型的数据:

  • Repetition and definition levels
  • 字典索引
  • 数据页中的布尔值,作为 PLAIN 编码的替代方案
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值