Parquet文件格式,已成为大数据存储的默认选择
高效存储与快速查询的完美结合
在数据量爆炸式增长的今天,如何高效地存储和处理海量数据成为了每个数据团队面临的挑战。面对这些挑战,Apache Parquet自2013年问世以来,迅速崛起为大数据存储领域的事实标准。但究竟是什么让Parquet格式如此受欢迎?它背后有哪些精妙的设计?今天,我们就来深入解析Parquet文件格式。
行存储 vs 列存储:根本性的差异
要理解Parquet的优势,我们首先需要了解行存储和列存储的根本区别。
在传统的行存储格式(如CSV、Avro)中,数据按行序列进行存储。这意味着每一行的所有字段值都连续存储在一起。这种存储方式适合事务处理(OLTP),其中需要频繁地访问整行记录。
与此相反,列存储格式(如Parquet)将每一列的数据单独存储。这种存储方式特别适合分析型查询(OLAP),因为大多数分析查询通常只涉及表中的部分列,而不需要访问所有列。
直观比喻:想象一下,行存储就像把一件件完整的衣服(整行记录)挂在衣柜里,而列存储则是将所有的袖子、衣身和纽扣(列)分别存放在不同的抽屉中。当只需要修改袖口设计时,后者显然更高效。
Parquet的核心设计:不只是简单的列存储
Parquet最精妙的地方在于,它并不仅仅是简单的列存储,而是引入了一个多层次的结构,将行和列的优势结合起来。
三层核心结构
一个Parquet文件包含三个相互关联的层次结构:
行组:每个文件被划分为多个行组,每个行组包含一定数量的行(默认为100万行)。行组是数据垂直和水平分区的单元。 列块:在每个行组中,每列都有一个对应的列块,包含该列在行组内的所有值。 页:每个列块又被划分为页(默认最大为1MB)。页是Parquet文件中最小的读写单元,有两种类型:数据页和字典页。
这种设计使得Parquet能够实现投影下推和谓词下推,这是其高性能的关键。
投影下推:查询只需读取查询中涉及的列,而不是整个行,从而大大减少I/O操作。 谓词下推:Parquet文件包含行组级别的统计信息(如每列的最小值和最大值),查询引擎可以利用这些信息跳过不满足条件的行组。
投影指的是SQL 语言中的SELECT语句——查询需要哪些列。回到之前的例子,我们只需要 Product 和 Country 列,因此引擎可以跳过扫描其余列。
谓词指的是SQL 语言中的WHERE子句——它用于确定哪些行满足查询中定义的条件。在本例中,我们只对 T 恤感兴趣,因此引擎可以完全跳过扫描第 2 行组,因为其中 Product 列的所有值都等于“socks”!
举例说明:假设你有一个包含1亿行数据的销售表,你需要查询"2023年来自北京的客户数量"。使用Parquet格式,查询引擎可以:
只读取"客户地区"和"销售日期"列(投影下推) 跳过所有"销售日期"不在2023年范围内的行组(谓词下推)
结果可能是,它只需要读取总数据量的10%,查询速度自然大幅提升。
高效的压缩与编码技术
Parquet的另一个优势是其出色的压缩能力,这得益于列存储的特性和多种编码技术的应用。
列存储的自然优势
由于同一列中的数据具有相同的数据类型和相似的值,它们比行存储中的混合数据更易压缩。想象一下尝试压缩一本电话簿:按列压缩(所有姓氏在一起)会比按行压缩(姓氏、名字、电话号码交替出现)高效得多。
Parquet使用的编码技术
字典编码:Parquet为列中的不同值构建字典,然后用字典索引替换实际值。这对于低基数列(如"国家"、"产品类型")特别有效。 行程长度编码:通过将连续的重复值替换为单个值和计数,进一步压缩数据。 位打包:对于小范围整数值,使用尽可能少的位数来存储每个值。
支持的压缩算法
Parquet支持多种压缩算法,让你在压缩比和速度间做出权衡:
SNAPPY:提供均衡的压缩比和速度,是常用选择 GZIP:提供更高的压缩比,但速度较慢 ZSTD:较新的算法,提供良好的压缩比和速度 LZ4:注重速度,压缩比相对较低 BROTLI:提供高压缩比
这些压缩技术结合使用,通常可以使Parquet文件比等效的CSV文件小 75-90%,不仅节省存储空间,还减少I/O操作,提高查询性能。
Parquet实践:如何读写Parquet文件
了解了理论,让我们看看如何在实践中使用Parquet文件。
使用Python读写Parquet
Python中的Pandas库通过pyarrow或fastparquet后端提供了对Parquet文件的便捷支持。
环境准备:
pip install pandas pyarrow
写入Parquet文件:
import pandas as pd# 创建示例DataFrame
data = {
'name': ['Alice', 'Bob', 'Charlie'],
'age': [25, 30, 35],
'salary': [70000, 80000, 90000]
}
df = pd.DataFrame(data)
# 将DataFrame写入Parquet文件
df.to_parquet('example.parquet', engine='pyarrow', compression='snappy')
读取Parquet文件:
# 从Parquet文件读取数据
df_parquet = pd.read_parquet('example.parquet', engine='pyarrow')
print(df_parquet)
使用ClickHouse查询Parquet文件
如果你使用ClickHouse,可以直接查询Parquet文件,无需先导入数据:
-- 识别文件架构
DESCRIBETABLEfile('house_prices.parquet')-- 查询特定数据
SELECT
toYear(toDate(date)) ASyear,
round(avg(price)) AS price
FROMfile('house_prices.parquet')
WHERE town = 'LONDON'
GROUPBYyear
ORDERBYyearASC
性能优化技巧
为了从Parquet文件中获得最佳性能,考虑以下优化技巧:
合理设置行组大小:行组大小影响内存使用和读取性能。较大的行组(100万行)可以提高压缩率和读取效率,但需要更多内存。 选择适当的压缩算法:根据你的需求权衡压缩比和速度。Snappy通常是不错的默认选择,而GZIP适合存储成本是关键因素的场景。 合并小文件:避免大量小Parquet文件,因为每个文件的元数据都需要单独读取。理想情况下,每个Parquet文件应为几百MB。 按常用查询列排序:在写入前按常用过滤列对数据排序,可以提高谓词下推的效率。 明智使用字典编码:对于低基数列,字典编码非常有效,但对于高基数列可能会适得其反。
Parquet的生态系统和未来
Parquet已成为大数据生态系统中的基石技术。它与Apache Spark、Apache Hadoop、Apache Drill等主流数据处理框架无缝集成。此外,更现代的数据湖表格式如Delta Lake、Apache Iceberg和Apache Hudi实际上也是在Parquet之上构建的,为其添加了事务、版本控制和其他管理功能。
随着数据领域的不断发展,Parquet也在进化。最近的改进包括:
支持更高效的编码技术,如字节流分割 增强对嵌套数据类型的能力 与Apache Arrow更紧密的集成,提供高效的内存表示
总结
Parquet之所以能成为大数据存储的默认选择,是因为它巧妙地结合了列式存储、多层结构和高效压缩的优势,提供了无与伦比的查询性能和存储效率。无论你是数据科学家、数据分析师还是数据工程师,理解和掌握Parquet格式都将有助于你构建更高效的数据解决方案。
在这个数据驱动的时代,选择合适的存储格式就像为你的数据选择最合适的家——而Parquet无疑是目前最理想的选择之一。
🏴☠️宝藏级🏴☠️ 原创公众号『数据STUDIO』内容超级硬核。公众号以Python为核心语言,垂直于数据科学领域,包括可戳👉Python|MySQL|数据分析|数据可视化|机器学习与数据挖掘|爬虫等,从入门到进阶!
长按👇关注- 数据STUDIO -设为星标,干货速递