Hive - 数据存储格式详解6(PARQUET格式)
六、PARQUET 格式
1,基本介绍
(1)Parquet 是一种新型的与语言无关的,并且不和任何一种数据处理框架绑定的列式存储结构,适配多种语言和组件。Parquet 数据存储格式可以在 Hive、Impala、Spark 等计算引擎中使用。
(2)Parquet 的存储格式如下图所示:
- Parquet 格式的文件中包含了多个 Row Group。
- 每个 Row Group 内部包含了多个 Column。
- Column 内部包含了多个 Page。
- Page 内部存储了具体的数据内容。
(3)上面图看起来稍微有点复杂,简化一下就是下面这样的:
- 可以看出 Parquet 存储格式比 ORC 更复杂一些,多封装出来一层 Page。ORC 存储格式是把数据内容直接存储到了 Column 这一层。
(4)在创建 PARQUET 数据存储格式表的时候,和 ORC 格式一样,所有关于 PARQUET 的参数都在建表语句中的 tblproperties 中指定,不需要在 hive 的命令行中使用 set 命令指定了。
注意:tblproperties 中可以指定多个参数,多个参数之间使用逗号分割即可。
create table t1( id int, name string ) stored as parquet tblproperties("parquet.compression"="uncompressed")
(5)目前 PARQUET 支持 uncompressed、snappy、gzip or lzo、brotli、lz4、zst 这几种压缩格式。默认是 uncompressed。
2,不使用压缩
(1)首先我们执行如下命令在 Hive 创建一个 PARQUET 存储格式的表,不使用压缩。注意:
- 这里需要在 stored as 后面指定 parquet,并且在 tblproperties 中指定 "parquet.compression"="uncompressed"。
- 针对 PARQUET 存储格式的表在控制压缩格式的时候是通过 parquet.compression 指定的,不需要在 hive 中设置 hive.exec.compress.output 参数了。
create external table stu_parquet_none_compress( id int, name string, city string ) row format delimited fields terminated by ',' lines terminated by '\n' stored as parquet location '/stu_parquet_none_compress' tblproperties("parquet.compression"="uncompressed");
(2)创建后执行如下命令确认一下这个表,如果 INPUTFORMAT 使用的是 MapredParquetInputFormat,说明这个表中需要存储 PARQUET 格式的数据。
show create table stu_parquet_none_compress;
(3)然后从普通表中查询数据导入到这个 PARQUET 存储格式的表中。
- 在这里我们设置 mapreduce.job.reduces=1,表示将结果数据写入到一个数据文件中,这也便于后面验证这个数据文件是否支持 Split。
普通表 stu_textfile 中的数据来源可以参考我前面写的文章:Hive - 数据存储格式详解 2(TextFile 格式)
set mapreduce.job.reduces=1; insert into stu_parquet_none_compress select id,name,city from stu_textfile group by id,name,city;
- 如果在任务执行过程中,特别是 reduce 阶段报“Caused by: java.lang.OutOfMemoryError: Java heap space”内存溢出错误,可以在命令执行前通过 set 命令调整内存限制,提高可用的内存大小:
set mapreduce.map.memory.mb=4096; set mapreduce.map.java.opts=-Xmx4096m; set mapreduce.reduce.memory.mb=4096; set mapreduce.reduce.java.opts=-Xmx4096m;
(4)查看结果数据可以发现,其体积为 1G,远小于原始的 TextFile 文件以及 SequenceFile 格式的 2G 大小,可以说明 PARQUET 在数据存储层面还是比较优秀的。
(5)接下来写一个 sql 查询表中的数据,验证是否支持切分:
select id,count(*) from stu_parquet_none_compress group by id;
- 可以看到产生了 8 个 map 任务,说明 PARQUET 格式的文件是支持切分的。
(6)最后,这个表中的数据验证完毕之后,建议删除一下数据,释放 HDFS 空间。
hdfs dfs -rm -r -skipTrash /stu_parquet_none_compress
3,使用 Gzip 压缩
(1)接下来我们构建一个新的压缩数据表,指定 Gzip 压缩格式。
create external table stu_parquet_gzip_compress( id int, name string, city string ) row format delimited fields terminated by ',' lines terminated by '\n' stored as parquet location '/stu_parquet_gzip_compress' tblproperties("parquet.compression"="gzip");
(2)接下来通过 insert into select 从普通表中查询数据,插入到压缩表中。
- 在这里我们设置 mapreduce.job.reduces=1,表示将结果数据写入到一个数据文件中,这也便于后面验证这个数据文件是否支持 Split。
注意:为了能够控制任务最终产生的数据文件个数,在这里通过 mapreduce.job.reduces 来控制,并且 SQL 语句中需要有可以产生 shuffle 的操作,如果是普通的 select 语句,最终是不会产生 Reduce 任务的,那么 mapreduce.job.reduces 这个参数就无法生效了。
set mapreduce.job.reduces=1; insert into stu_parquet_gzip_compress select id,name,city from stu_textfile group by id,name,city;
- 如果在任务执行过程中,特别是 reduce 阶段报“Caused by: java.lang.OutOfMemoryError: Java heap space”内存溢出错误,可以在命令执行前通过 set 命令调整内存限制,提高可用的内存大小:
set mapreduce.map.memory.mb=4096; set mapreduce.map.java.opts=-Xmx4096m; set mapreduce.reduce.memory.mb=4096; set mapreduce.reduce.java.opts=-Xmx4096m;
- 可以看到最终产生的数据文件大小为 318M:
(3)接下来写一个 sql 查询压缩表中的数据,确认一下是否验证是否支持切分:
select id,count(*) from stu_parquet_gzip_compress group by id;
- 结果发现只产生了 2 个 Map 任务,说明说明 Parquet 格式带压缩的文件也支持切分。
(6)最后,这个压缩表中的数据查看后最好删除一下,这样可以释放 HDFS 存储空间。
hdfs dfs -rm -r -skipTrash /stu_parquet_gzip_compress
4,使用 Snappy 压缩
(1)接下来我们构建一个新的压缩数据表,指定 Snappy 压缩格式。
create external table stu_parquet_snappy_compress( id int, name string, city string ) row format delimited fields terminated by ',' lines terminated by '\n' stored as parquet location '/stu_parquet_snappy_compress' tblproperties("parquet.compression"="snappy");
(2)接下来通过 insert into select 从普通表中查询数据,插入到压缩表中。
- 在这里我们设置 mapreduce.job.reduces=1,表示将结果数据写入到一个数据文件中,这也便于后面验证这个数据文件是否支持 Split。
注意:为了能够控制任务最终产生的数据文件个数,在这里通过 mapreduce.job.reduces 来控制,并且 SQL 语句中需要有可以产生 shuffle 的操作,如果是普通的 select 语句,最终是不会产生 Reduce 任务的,那么 mapreduce.job.reduces 这个参数就无法生效了。
set mapreduce.job.reduces=1; insert into stu_parquet_snappy_compress select id,name,city from stu_textfile group by id,name,city;
- 如果在任务执行过程中,特别是 reduce 阶段报“Caused by: java.lang.OutOfMemoryError: Java heap space”内存溢出错误,可以在命令执行前通过 set 命令调整内存限制,提高可用的内存大小:
set mapreduce.map.memory.mb=4096; set mapreduce.map.java.opts=-Xmx4096m; set mapreduce.reduce.memory.mb=4096; set mapreduce.reduce.java.opts=-Xmx4096m;
- 此时发现结果数据文件有 701M。比 Gzip 的压缩性能差一些,因为 Gzip 的压缩比确实比 Snappy 高,Snappy 最主要的优点是压缩和解压速度快。
(3)接下来写一个 sql 查询压缩表中的数据,确认一下是否验证是否支持切分:
select id,count(*) from stu_parquet_snappy_compress group by id;
- 结果发现产生了 3 个 Map 任务,说明 PARQUET + Snappy 压缩的文件也支持切分。
(4)最后,这个压缩表中的数据查看后最好删除一下,这样可以释放 HDFS 存储空间。
hdfs dfs -rm -r -skipTrash /stu_parquet_snappy_compress