Parquet 存储格式

1.介绍

Apache Parquet 是 Hadoop 生态圈中一种新型列式存储格式,它可以兼容 Hadoop 生态圈中大多数计算框架(Mapreduce、Spark 等),被多种查询引擎支持(Hive、Impala、Drill 等),并且它是语言和平台无关的。

2.特点:列式存储

Parquet文件存储格式的特点:列式存储,相对于关系数据库中通常使用的行式存储,在使用列式存储时每一列的所有元素都是顺序存储的
列式存储相对于行式存储的优点:

  1. 查询的时候不需要扫描全部的数据,而只需要读取每次查询涉及的列,这样可以将I/O 消耗降低 N 倍。

  2. 由于每一列的成员都是同构的,可以针对不同的数据类型使用更高效的数据压缩算 法,进一步减小 I/O。

  3. 适配多种计算框架Parquet 是语言无关的,而且不与任何一种数据处理框架绑定在一起,适配多种语言和组件,能够与 Parquet 配合的组件有:

    • 查询引擎: Hive, Impala, Pig, Presto, Drill, IBM Big SQL
    • 计算框架: MapReduce, Spark, Cascading, Kite

在这里插入图片描述

3.文件格式

Parquet 文件是以二进制方式存储的,是不可以直接读取和修改的,Parquet 文件是自解析的,文件中包括该文件的数据和元数据。
Parquet 文件中存在如下几个概念:

  1. 行组(Row Group):按照行将数据物理上划分为多个单元,每一个行组包含一定的行数,在一个 HDFS 文件中至少存储一个行组,Parquet 读写的时候会将整个行组缓存在内存中,所以每一个行组的大小是由内存大的小决定的。
  2. 列块(Column Chunk):在一个行组中每一列保存在一个列块中,行组中的所有列连续的存储在这个行组文件中。不同的列块可能使用不同的算法进行压缩。
  3. 页(Page):每一个列块划分为多个页,一个页是最小的编码的单位,在同一个列块 的不同页可能使用不同的编码方式。

例:下图展示了一个 Parquet 文件的内容,一个文件中可以存储多个行组,文件的首位都是该文件的 Magic Code,用于校验它是否是一个 Parquet 文件,Footer length 了文件元数据的大小,通过该值和文件长度可以计算出元数据的偏移量,文件的元数据中包括每一个行组的元数据信息和该文件存储数据的 Schema 信息。

在这里插入图片描述

4.常见的四种压缩格式

在这里插入图片描述

Spark默认使用snappy压缩格式,需要改变可以在SparkConf/Session程序入门中设置:

在这里插入图片描述

5.Spark读写Parquet文件

val peopleDF = spark.read.json("examples/src/main/resources/people.json")
// DataFrames can be saved as Parquet files, maintaining the schema information
peopleDF.write.parquet("people.parquet")
val parquetFileDF = spark.read.parquet("people.parquet")

关于分区:

By passing path/to/table to either SparkSession.read.parquet or SparkSession.read.load, Spark SQL will automatically extract the partitioning information from the paths.
Notice that the data types of the partitioning columns are automatically inferred. Currently,numeric data types, date, timestamp and string type are supported. Sometimes users may not want to automatically infer the data types of the partitioning columns. For these use cases, the automatic type inference can be configured by spark.sql.sources.partitionColumnTypeInference.enabled, which is default to true. When type inference is disabled, string type will be used for the partitioning columns.
Starting from Spark 1.6.0, partition discovery only finds partitions under the given paths by default. For the above example, if users pass path/to/table/gender=male to either SparkSession.read.parquet or SparkSession.read.load, gender will not be considered as a
partitioning column. If users need to specify the base path that partition discovery should start with, they can set basePath in the data source options. For example, when path/to/table/gender=male is the path of the data and users set basePath to path/to/table/, gender will be a partitioning column.

有些数据不能连续存储,主要是由于**内存管理机制、数据结构特性、运行时动态性、以及硬件限制**等因素共同决定的。连续存储虽然效率高,但并不是所有数据都适合或可以连续存储。 --- ## 🧠 为什么有些数据不能连续存储? ### 1. **数据大小不固定** - 某些数据结构的元素大小不固定(如字符串、变长结构体等),无法统一连续排列; - 例如:链表节点的大小可能因动态分配而不同。 ### 2. **动态增长需求** - 如果数据在运行时频繁增删,连续存储的数组结构在扩容时需要重新分配内存并复制数据,效率低; - 链表等结构通过离散存储避免了这个问题。 ### 3. **内存碎片问题** - 连续存储要求一块**未被占用的连续内存块**; - 频繁分配和释放内存后,会产生**内存碎片**,即使有足够内存,也可能找不到连续的大块内存。 ### 4. **多线程/并发访问** - 在多线程环境中,多个线程可能同时请求内存分配; - 连续分配需要协调内存空间,容易造成竞争和效率下降; - 离散分配更灵活,适合并发操作。 ### 5. **虚拟内存与地址映射机制** - 现代操作系统使用**虚拟内存机制**,每个进程看到的地址空间是连续的,但实际物理内存是离散的; - 因此,**程序看到的“连续”只是虚拟地址的抽象**,实际物理内存不一定连续。 ### 6. **硬件限制** - 某些硬件平台对内存访问有特殊限制(如嵌入式系统); - 无法保证大块连续内存可用,因此采用离散结构更安全可靠。 --- ## 📌 举例说明 ### ✅ 连续存储适用场景(数组): ```c int arr[100]; // 100个int连续存储 ``` - 优点:访问快、缓存命中率高; - 缺点:扩容困难、插入删除慢。 ### ❌ 离散存储适用场景(链表): ```c struct Node { int data; struct Node* next; }; ``` - 优点:插入删除快、内存利用率高; - 缺点:访问慢、缓存不友好。 --- ## ✅ 总结 > 数据不能连续存储的原因包括:数据大小不固定、动态增长需求、内存碎片、并发访问、虚拟内存机制和硬件限制等。虽然连续存储访问效率高,但离散存储在灵活性、动态性和并发性方面具有优势,因此在实际系统中两者互补使用。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值