Parquet省20倍?为啥你只省了3倍
📋 总体概括
“直接用Parquet”是数据工程里被重复最多、被解释最少的一句话。本文拆开列裁剪、压缩、编码三层机制,说清Parquet赢在哪里、什么时候反而不赢,以及它为何成为湖仓时代的底座文件格式。
📄 正文
导语
数据工程圈流传最广的建议,可能就是那句 "Just use Parquet"——被重复了一万次,被解释清楚的次数却屈指可数。它确实是好建议,但如果不知道它为什么赢,你就无法判断它什么时候不赢:要么把本该留在CSV的文件白白转了一遍格式,要么满心期待20倍的收益,结果只拿到3倍,开始怀疑人生。这篇文章把Apache Parquet赢的三层机械原因拆开讲透,再聊聊它什么情况下会输,以及它为什么已经悄悄成为整个湖仓时代的地基。
📊 列式存储:查询只读10%,这才是全部戏法
一句金句开场:CSV按行读,Parquet按列读,仅此而已。
先看一个最常见的场景。你手头有一张用户表,字段包括 id、name、email、signup_date、revenue,存成CSV大概长这样:
`
id,name,email,signup_date,revenue
1,Alice,alice@x.com,2026-01-15,127.50
2,Bob,bob@y.com,2026-01-16,340.00
`
现在你要算总收入,SELECT SUM(revenue)。对CSV来说,读取器必须从头走到尾,把每一行的名字、邮箱、日期全部从磁盘读出来、解析一遍,才能凑出revenue这一列。每一行都是一次完整的通行。
Apache Parquet的做法完全相反:它按列存储。同一条查询只读revenue这一列的数据块,其他什么都不碰。而绝大多数分析型查询只会触碰到表里10%到20%的列——这意味着CSV永远读100%的文件,Parquet大概率只读10%到20%。
这就是那些漂亮基准测试数字背后的全部戏法。而且有一个容易被忽略的推论:这个优势会随着表变宽而放大。表格从20列变成200列,查询触达的列占比往往更低,列式的收益就越大。反过来,一张只有三四列的窄表,你怎么转格式都省不出多少东西。
产业逻辑上,这一层直接对应真金白银。云上对象存储的扫描是要付费的,查询引擎扫得越少,账单越薄。据多位在云厂商做数据平台的工程师私下聊,不少团队下决心把全量历史数据从CSV迁到Parquet,临门一脚往往不是什么架构理想,而是月底那张按扫描量计费的账单。
🗜️ 压缩的物理直觉:同类的东西坐在一起
第二个原因更朴素:压缩靠模式吃饭,而CSV的行几乎全是噪声。
看CSV那一行:一个字符串、一个日期、一个浮点数、两个整数,被硬挤在同一行里。对压缩算法来说,这种异质数据的交错排列接近随机,找不到什么可利用的规律,压完也省不了几个字节。
Parquet把存储粒度换成了列。一列就是一千万个浮点数,或者一千万个日期,或者一千万个国家名。同类数据紧密相邻,模式呼之欲出:日期有单调递增的趋势,国家名只有几十个取值在反复出现,浮点数往往集中在一个区间。压缩算法对付这种数据,效果和对付交错行完全不是一个量级。
这也解释了一个很多人踩过的坑:为什么别人博客写20倍,你只压出3倍。压缩比不是一个常数,它是数据特征的函数。低基数的枚举列、重复度高的文本列,压缩比可以非常高;高基数的ID列、随机浮点数列,能省的就有限。你的表里这类列占多少,决定了你最后落在3倍还是20倍之间。拿着别人的数字当自己的预期,是格式选型里最常见的错误。
工程上的连带收益也很实际:文件更小,意味着网络传输更少、存储成本更低、操作系统页缓存能装下更多热数据。对分析型负载来说,这三件事每一件都在直接改善查询延迟和成本。
⚡ 被忽略的第三层:编码与谓词下推
前两层被讲烂了,第三层却最少有人提:列式不只是省空间,它还让数据库的许多老招式重新生效。
首先是编码。列内数据同质之后,除通用压缩外还可以做专门的轻量编码——重复值多的列可以记录“这个值连续出现了N次”,取值少的列可以用字典把长字符串替换成短整数。这些编码在有列边界的前提下才能实施,行式存储里根本无从下手。
其次是跳读。Parquet文件内部带有元数据,记录着每个数据块中该列的最小值和最大值。查询带上过滤条件时,引擎看一眼元数据就知道哪些块整块都可以跳过,根本不用读。扫描量从“只读所需列”进一步收缩到“只读所需列中符合条件的那部分块”。
这一层是Apache Spark、DuckDB这类现代分析引擎性能故事的一半:引擎的向量化执行再快,底下的文件格式如果不配合,也是白搭。格式和引擎是在共同演进的,这也是为什么用Parquet时,选一个能吃透其元数据的引擎同样重要。
把三层收益叠起来看,可以列成一张表:
| 维度 | CSV | Parquet |
|---|---|---|
| 存储组织 | 按行,异质数据交错 | 按列,同类数据相邻 |
| 读取模式 | 任何查询读100%文件 | 只读查询触达的列与块 |
| 压缩与编码 | 近似噪声,收益有限 | 通用压缩加列内编码,双重收益 |
| 跳读能力 | 无元数据,只能硬扫 | 依赖块级统计元数据跳过无关数据 |
| 收益随表宽 | 不变 | 表越宽、查询列越少,收益越大 |
⚠️ 什么时候Parquet反而不如CSV
一 flush 金句:不知道一种格式什么时候会输,就等于没懂这种格式。
第一个反例是小文件。Parquet的收益建立在“按列块批量读”的前提上,如果你的管道每分钟吐出一个几百KB的小Parquet文件,列块的统计元数据和顺序读的优势都会被元数据开销和小文件合并的折腾吃掉。在对象存储上,小文件问题是比格式选择更先要解决的病。
第二个反例是写路径。Parquet的列式编码和压缩是需要缓冲和计算的,写入成本明显高于直接追加一行文本。高频追加、逐条写入的流式场景,很多团队会选择先落缓冲层,攒够批量再转列式——这本身就是承认“写入时行式便宜,读取时列式划算”的分工。
第三个反例是互操作性。CSV没有类型、没有schema约束,任何电子表格软件、任何脚本、任何不挑剔的工具都能直接打开。数据要给人看、要交给下游说不清是什么系统的场景,CSV的“笨”恰恰是它的通行证。把这类文件强行转成Parquet,得到的是更少的人能打开它。
所以那句被念烂的 "Just use Parquet",更准确的版本应该是:面向机器批量分析的、写了基本不再改的数据,用Parquet;面向人、面向零散交换、面向高频小量写入的数据,CSV仍然有一席之地。 转格式之前先问一句:这份数据的读取模式到底是什么?
🏗️ 从文件格式到湖仓底座:Parquet的产业位置
最后一个问题:为什么一个文件格式,值得写一篇产业分析?
因为过去几年湖仓生态的爆发,几乎全部叠在Parquet之上。公开资料显示,Apache Parquet在2013年前后由Twitter与Cloudera贡献给Apache社区;十年后,Apache Iceberg、Delta Lake、Apache Hudi这三大开放表格式,底下的物理文件层清一色默认Parquet。表格式管元数据、事务和版本,Parquet管实际的列式存取,两者分工明确。
这个分层结构对产业的意义在于:Parquet成了数据要素事实上流动的“集装箱标准”。不管上层引擎是Spark还是DuckDB,不管表格式选Iceberg还是Delta,底下交换的都是同一种列式文件。数据要跨团队、跨平台、跨云流转时,Parquet是摩擦最小的公共语言。对一个谈数据要素流通的行业来说,这个位置比任何基准测试数字都重要。
而它保持这个位置靠的不是营销,是那条朴素的机械逻辑:列裁剪、压缩、编码、跳读,每一层都是可以用自己的数据和账单验证的。开源社区把它做成了默认选项,云厂商把它做成了导出格式的默认值,一个文件格式就这样长成了基础设施。
小结
Parquet赢,赢在三个不需要论文就能讲清的机械原因:按列读所以只读10%到20%;同类数据相邻所以压缩好;列内编码与块级统计让跳读成为可能。它的收益随表变宽而放大,随数据重复度变化而波动,也随时会在小文件、高频写入和人类直接消费的场景里输给CSV。理解机制,才能算清自己那份数据到底能省多少。往大了看,Parquet已经从“一个好格式”变成了湖仓的公共底座和数据的集装箱标准——读懂它,才真正读懂了这个行业存放数据的方式。
本文由本站 AI 辅助聚合生成,原始来源如下: