列式压缩:轻量编码怎么选、Parquet 与 ORC 怎么写、编码数据上怎么算
内容提要
本文比较列存轻量编码与通用压缩。在相同TPC-H数据上,Parquet默认字典对有序整数压缩效果反而更差,DELTA_BINARY_PACKED更小;ORC对整数不建字典,字符串仅判断一次,容易选错编码。排序可大幅改变压缩效果。zstd的收益多来自文本列。DuckDB采用全量分析,BtrBlocks通过采样选择编码更优,但采样存在固定开销与偏差。
延伸解读
编码选择比压缩算法更重要
文章通过TPC-H数据表明,同一列使用不同轻量编码,字节数可能相差数倍。例如有序整数列l_orderkey,Parquet默认字典编码为1,894,373字节,而DELTA_BINARY_PACKED仅377,246字节。这提醒我们,在考虑通用压缩算法(如zstd)之前,应先关注列存格式的轻量编码选择,因为它直接利用数据类型和分布规律,往往能带来更大的压缩收益。
排序对压缩效果影响显著
将表按低基数列排序后,Parquet的l_returnflag列块从146,567字节降至71字节,ORC的同一列也从311,391字节降至7,097字节。排序能增加相邻值的重复性,使游程编码和差分编码更有效。但不同编码受益程度不同:字典编码按出现顺序编号,无法利用排序后的顺序关系;而差分编码则能大幅受益。因此,合理设计排序键可以显著提升压缩率。
通用压缩的收益集中在文本列
在TPC-H lineitem上,zstd将Parquet文件从29,204,234字节压到15,208,332字节,其中12,674,942字节的节省来自自由文本列l_comment。其余15列仅减少约12%。这说明通用压缩主要对高基数、无结构的文本有效,而对已经过轻量编码的数值列收益有限。因此,按列决定是否使用通用压缩比按文件统一压缩更合理。
采样选择编码存在偏差风险
BtrBlocks式采样选择在1%样本上,160个块中有140个与穷举最优一致,总字节仅多0.29%。但选错的20个块全部来自l_discount和l_tax,原因是字典等方案有固定开销,在小样本上比例被高估,导致估算反转。这表明采样选择对包含固定开销的编码可能产生系统性偏差,尤其在低基数数据上,需要更精细的样本设计或修正方法。
Q&A
Parquet 和 ORC 在整数列编码上有什么主要区别?
Parquet 默认对整数列先尝试字典编码,字典页达到 1 MiB 后回退为 PLAIN;ORC 的整数列只有 RLEv1 和 RLEv2 两种编码,不建字典。因此对于有序整数,Parquet 的字典可能比 DELTA_BINARY_PACKED 大很多,而 ORC 的 RLEv2 在低基数整数上可能不如 Parquet 字典。
为什么 Parquet 对有序整数使用字典编码反而效果更差?
因为字典编码按值出现顺序分配编号,不保留值的大小关系,无法利用有序性。对于有序整数,相邻差分很小,DELTA_BINARY_PACKED 能大幅压缩,而字典需要存储所有不同值及其索引,导致体积更大。例如 l_orderkey 列,字典版比 DELTA_BINARY_PACKED 大 5 倍。
ORC 如何决定是否为字符串列使用字典编码?
ORC 在第一个行组(默认 10,000 行)结束时检查不同值比例,如果比例小于等于阈值(默认 0.8)则使用字典编码,否则使用直接编码。这个判断只做一次,之后不再更改。
排序如何影响列式存储的压缩效果?
排序可以显著改变压缩效果。将表按低基数列排序后,这些列会产生长游程,适合 RLE 编码,压缩率大幅提升。例如 l_returnflag 列在排序后,Parquet 列块从 146,567 字节降到 71 字节。但排序也可能打乱原本有序的列,导致其压缩效果变差。
BtrBlocks 的采样选择编码与 Parquet 的固定规则相比有什么优势?
BtrBlocks 通过采样(1% 数据)为每个块选择最优编码,在 TPC-H 数据上,140/160 个块与穷举最优一致,总字节仅比最优多 0.29%。而 Parquet 使用固定规则(先字典后回退),可能选择次优编码。但采样存在固定开销偏差,对于有固定开销的编码(如字典)在小样本上可能被高估。
在编码数据上直接执行计算能节省多少工作?
在字典编码和 RLE 上直接执行可以避免解码。例如,对于等值查询,只需在字典中查找一次,然后比较整数编码,节省字符串比较;对于聚合查询,RLE 允许一次处理整个游程。在排序后的数据上,GROUP BY 操作从 600,572 次哈希更新减少到 3 次。