Impala 表使用 Parquet 文件格式
Impala 帮助你创建、管理、和查询 Parquet 表。Parquet 是一种面向列的二进制文件格式,设计目标是为 Impala 最擅长的大规模查询类型提供支持(Parquet is a column-oriented binary file format intended to be highly efficient for the types of large-scale queries that Impala is best at)。Parquet 对于查询扫描表中特定的列特别有效,例如查询一个包含许多列的"宽"表,或执行需要处理列中绝大部分或全部的值的如 SUM(),AVG() 等聚合操作(Parquet is especially good for queries scanning particular columns within a table, for example to query "wide" tables with many columns, or to perform aggregation operations such as SUM() and AVG()that need to process most or all of the values from a column)。每个数据文件包含行集(行组)的值(Each data file contains the values for a set of rows (the "row group"))。在数据文件里,每一列的值都被重组,以便他们相邻,从而对这些列的值进行良好的压缩(the values from each column are organized so that they are all adjacent, enabling good compression for the values from that column)。针对 Parquet 表的查询可以快速并最小 I/O 的从任意列获取并分析这些数据(table can retrieve and analyze these values from any column quickly and with minimal I/O)。
参考以下章节,以了解关于 Impala 表如何使用 Parquet 数据文件的详细信息:
避免对 Parquet 表使用 INSERT...VALUES 语法,因为 INSERT...VALUES 为每一个 INSERT...VALUES 语句产生一个包含少量数据的单独的数据文件,而 Parquet 的实力在于它以 1GB 块的方式处理数据(压缩、并行、等等操作)(and the strength of Parquet is in its handling of data (compressing, parallelizing, and so on) in 1GB chunks)。
加载数据到 Parquet 表是内存密集型操作,因为接受的数据会被缓存一直到达到 1GB 大小,然后这些块的数据在写出之前在内存中被组织和压缩。当插入数据到分区 Parquet 表中时,内存消耗会更大,因为对每一种分区键值的组合都写入一个单独的数据文件,同时可能有几个 1GB 的块在操作(potentially requiring several 1GB chunks to be manipulated in memory at once)。
当向分区 Parquet 表插入数据时,Impala 在节点之间重新分布数据以减少内存消耗。但当插入操作时,你可能仍然需要临时增加 Impala 专用的内存量,或者把加载操作拆分到几个 INSERT 语句中,或者两种方式都采用(You might still need to temporarily increase the memory dedicated to Impala during the insert operation, or break up the load operation into several INSERT statements, or both)。
Impala Parquet 表的查询性能
Parquet 表的数据是划分成一个个的 1GB 的数据文件的("行组(row groups)"),查询时读取以压缩格式存储的每一列的数据,并消耗 CPU 解压数据。因此 Parquet 表的查询性能依赖于查询中 SELECT 列表和 WHERE 子句中需要处理的列的个数,以及是否可以跳过较多的数据文件(分区表),降低 I/O 并减少 CPU 负载(Query performance for Parquet tables depends on the number of columns needed to process the SELECT list and WHERE clauses of the query, the way data is divided into 1GB data files ("row groups"), the reduction in I/O by reading the data for each column in compressed format, which data files can be skipped (for partitioned tables), and the CPU overhead of decompressing the data for each column)。
例如,下面对 Parquet 表的查询是高效的:
select avg(income) from census_data where state = 'CA';
复制代码
这个查询只处理一大堆列中的两个列。假如表是根据 STATE 分区的,它甚至更有效率,因为查询仅仅需要对每个数据文件读取和解码 1 列,并且它可以只读取 state 'CA' 分区目录下的数据文件,跳过其他 states 的、物理上的位于其他目录的所有数据文件。
Parquet 文件格式非常适合包含许多列,并且绝大部分查询只设计表的少量列表。就如在 How Parquet Data Files Are Organized中描述的那样,Parquet 数据文件的物理分布使得对于许多查询 Impala 只需要读取数据的一小部分。当你结合使用 Parquet 表和分区时,这一性能受益会更加扩大。基于 WHERE 子句中引用的分区键的比较值,Impala 可以跳过特定分区实体的数据文件。例如,分区表上的查询通常基于年、月、日、或者地理位置的列进行时间段的分析(queries on partitioned tables often analyze data for time intervals based on columns such as YEAR, MONTH, and/or DAY, or for geographic regions)。请记住 Parquet 数据文件使用 1GB 的块大小,所以在确定如何精细的分区数据时,请尝试找到一个粒度,每一个分区都有 1GB 或更多的数据,而不是创建大量属于多个分区的的小文件。
[localhost:21000] > create table parquet_gzip like raw_text_data;
[localhost:21000] > set PARQUET_COMPRESSION_CODEC=gzip;
[localhost:21000] > insert into parquet_gzip select * from raw_text_data;
Inserted 1000000000 rows in 1418.24s
复制代码
未压缩的 Parquet 表
假如你的数据压缩作用非常有限,或者你想避免压缩和解压缩实体的 CPU 负载,请在插入数据前设置 PARQUET_COMPRESSION_CODEC 查询选项为 none:
[localhost:21000] > create table parquet_none like raw_text_data;
[localhost:21000] > insert into parquet_none select * from raw_text_data;
Inserted 1000000000 rows in 146.90s
复制代码
压缩 Parquet 表的大小和速度
下面的例子演示了 10 亿条复合数据在数据大小和查询速度方面的差异,他们分别使用了不同的编解码器进行压缩(Here are some examples showing differences in data sizes and query speeds for 1 billion rows of synthetic data, compressed with each kind of codec)。与往常一样,使用你自己真实的数据集进行类似的测试。实际的压缩比、对应的插入和查询速度,将取决于实际数据的特征而有所不同。
在例子中,压缩方式从 Snappy 换到 GZip 能减少 40% 的大小,而从 Snappy 换到不压缩将增加 40% 的大小(In this case, switching from Snappy to GZip compression shrinks the data by an additional 40% or so, while switching from Snappy compression to no compression expands the data also by about 40%):
23.1 G /user/hive/warehouse/parquet_compression.db/parquet_snappy
13.5 G /user/hive/warehouse/parquet_compression.db/parquet_gzip
32.8 G /user/hive/warehouse/parquet_compression.db/parquet_none
复制代码
因为 Parquet 数据文件通常大小是 1GB 左右,每一个目录都包含不同数量的数据文件并安排不同的行组(each directory will have a different number of data files and the row groups will be arranged differently)。
Parquet 数据文件如何组织
尽管 Parquet 是一个面向列的文件格式,不要期望每列一个数据文件。Parquet 在同一个数据文件中保存一行中的所有数据,以确保在同一个节点上处理时一行的所有列都可用。Parquet 所做的是设置 HDFS 块大小和最大数据文件大小为 1GB,以确保 I/O 和网络传输请求适用于大批量数据(What Parquet does is to set an HDFS block size and a maximum data file size of 1GB, to ensure that I/O and network transfer requests apply to large batches of data)。
在成G的空间内,一组行的数据会重新排列,以便第一行所有的值被重组为一个连续的块,然后是第二行的所有值,依此类推。相同列的值彼此相邻,从而 Impala 可以对这些列的值使用高效的压缩技术(Within that gigabyte of space, the data for a set of rows is rearranged so that all the values from the first column are organized in one contiguous block, then all the values from the second column, and so on. Putting the values from the same column next to each other lets Impala use effective compression techniques on the values in that column)。
Note:
Parquet 数据文件的 HDFS 块大小是 1GB,与 Parquet 数据文件的最大大小相同,一边每一个数据文件对应一个 HDFS 块,并且整个文件可以在单个节点上处理,不需要任何远程读取。假如在文件复制时块大小重设为较低的值,你将发现涉及到这些文件的查询性能更低,并且 PROFILE 语句将会揭示一些 I/O 是次优的,会通过远程读取。参见 Example of Copying Parquet Data Files了解当复制 Parquet 数据文件时如何保留块大小的例子。
当 Impala 检索或测试特定列的数据时,它将打开所有的数据文件,但只会读取每一个文件中这些列的值连续存放的位置(but only reads the portion of each file where the values for that column are stored consecutively)。假如其他的列在 SELECT 列表或 WHERE 子句中列出,在同一个数据文件中同一行的所有列的数据都可用。