Impala 表使用 Parquet 文件格式

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)。 html

參考如下章節,以瞭解關於 Impala 表如何使用 Parquet 數據文件的詳細信息: node

在 Impala 中建立 Parquet 表

請使用相似下面的命令,建立名爲 PARQUET_TABLE_NAME 並使用 Parquet 格式的表,請替換爲你本身的表名、列名和數據類型: 算法

[impala-host:21000] > create table parquet_table_name(x INT, y STRING) STORED AS PARQUET;
  Note: 以前,STORED AS 子句須要使用 PARQUETFILE 關鍵字。在 Impala 1.2.2 及以上版本,可使用 STORED AS PARQUET。建議新的代碼使用 PARQUET 關鍵字。

或者,克隆現有表的列名和數據類型: sql

[impala-host:21000] > create table parquet_table_name LIKE other_table_name STORED AS PARQUET;

當建立了表以後,請使用相似下面的命令插入數據到表中,請再次使用你本身的表名: shell

[impala-host:21000] > insert overwrite table parquet_table_name select * from other_table_name;

假如 Parquet 表具備與其餘表不一樣數量的列或不一樣的列名,請在對其餘表的 SELECT 語句中指定列名而不是使用 * 來代替。 apache


加載數據到 Parquet 表


根據原始數據是否已經在 Impala 表中,或者在 Impala 以外存在原始數據文件,來選擇下面的技術加載數據到 Parquet 表裏。 緩存

假如你的數據已經在 Impala 或 Hive 表裏,多是在不一樣的文件格式或分區模式下,你能夠直接使用 Impala INSERT...SELECT 語法傳輸這些數據到 Parquet 表。你能夠在同一個 INSERT 語句中,對數據執行轉換、過濾、從新分區,以及其餘相似操做。參見 Snappy and GZip Compression for Parquet Data Files 瞭解一些演示如何插入數據到 Parquet 的例子。 安全

當插入到分區表中,特別是使用 Parquet 文件格式的,你能夠在 INSERT 語句中包含一個提示(hint)以減小同時寫入 HDFS 文件的數量,以及爲不一樣的分區保存數據提供的 1GB 內存緩存的個數(and the number of 1GB memory buffers holding data for individual partitions)。請將 hint 關鍵字 [SHUFFLE]/[NOSHUFFLE] 放在緊跟表名以後。當 INSERT 語句運行失敗,或當全部節點上試圖構造全部分區的數據而致使的低效時,使用 [SHUFFLE] 提示。這一提示僅在 Impala 1.2.2 及以上版本可用。 服務器

Parquet 表的任意 INSERT 語句須要 HDFS 文件系統中有足夠寫入一塊的空間。由於 Parquet 數據文件默認 1GB/塊, 假如你的 HDFS 所剩無幾,那麼 INSERT 可能失敗(即便很是少許的數據)。 網絡

避免對 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)。

假如你具備一個或多個 Impala 以外生成的 Parquet 數據文件,使用如下某一個方法,你能夠快速的使這些數據能夠在 Impala 中查詢:

  • 使用 LOAD DATA 語句移動單個數據文件或某個目錄下全部數據文件到 Impala 表對應的數據目錄中。這不會驗證或轉換數據。原始的數據文件必須在 HDFS 中,而不能是在本地文件系統中。
  • 使用包含 LOCATION 子句的 CREATE TABLE 語句建立一個數據繼續存放在 Impala 數據目錄以外的表。原始的數據文件必須在 HDFS 中,而不能是在本地文件系統中。爲了增強安全性,假如這些數據是長時間存在並被其餘應用重用,你可使用 CREATE EXTERNAL TABLE 語法,這樣 Impala DROP TABLE 語句不會刪除這些數據文件。
  • 假如 Parquet 表已經存在,你能夠直接複製 Parquet 數據文件到表的目錄中,而後使用 REFRESH 語句使得 Impala 識別到新添加的數據。請記住對 Parquet 數據文件使用 hdfs distcp -pb 命令而不是 -put/-cp 操做,以保留Parquet 數據文件的 1GB 塊大小。參見 Example of Copying Parquet Data Files 瞭解這種操做的例子。

假如數據在 Impala 以外,而且是其餘格式,請結合使用以前提到的技術。首先,使用 LOAD DATA 或 CREATE EXTERNAL TABLE ... LOCATION 語句把數據存入使用對應文件格式的 Impala 表中。而後,使用 INSERT...SELECT 語句複製數據到 Parquet 表中,做爲這一處理過程的一部分轉換爲 Parquet 格式。

加載數據到 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)。

  Note: 以前全部的技術都假定你所加載的數據與你的目標表的結構匹配,包括列的順序,列的名稱,以及分區佈局(layout)。爲了轉換或重組數據,開始先加載數據到與底層數據匹配的 Parquet 表中,而後使用如 CREATE TABLE AS SELECT 或 INSERT ... SELECT 之一的表複製技術來從新排序或重命名列,拆分數據到多個分區等等。例如加載單個包含全部數據的 Parquet 文件到分區表中,你應當使用包含動態分區的 INSERT ... SELECT 語句來讓 Impala 以對應的分區值建立單獨的數據文件;例子參見  INSERT Statement



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 的、物理上的位於其餘目錄的全部數據文件。


如下查詢至關低效:
select * from census_data;
Impala 將不得不讀取每個 1GB 數據文件的整個內容,並解壓每個行組中每一列的內容,浪費了面向列格式的 I/O 優化。對於 Parquet 表,這一查詢可能比其餘格式的表更快,可是它沒有從 Parquet 數據文件格式獨有的優點中受益。

Parquet 表的分區

就如 Partitioning 中的描述同樣,對 Impala 來講,分區是一項重要而通用的性能技術。本章節介紹一些關於分區 Parquet 表的性能考慮。

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 或更多的數據,而不是建立大量屬於多個分區的的小文件。

插入到分區 Parquet 表示一個資源密集型(resource-intensive)操做,由於每個 Impala 節點對於每個分區鍵的不一樣組合均可能潛在的寫一個單獨的數據文件。大量的同時打開的文件數可能會達到 HDFS "transceivers" 限制。考慮採用如下技術,避免達到這一限制:

  • 使用包含特定值的 PARTITION 子句的單獨的 INSERT 語句加載不一樣的數據子集,如 PARTITION (year=2010)
  • 增長 HDFS 的 "transceivers" 值,有時候寫做 "xcievers" (sic)。在配置文件 hdfs-site.xml 中爲 dfs.datanode.max.xcievers 屬性。例如,假如你加載 12 年的數據,而分區包含年、月、日,甚至這個值設置爲 4096 也可能不夠。這一博客使用 HBase 例子做爲說明,探討了設置增長或減小這一值大小時的考慮
  • 在數據被複制的源表上採集列統計信息,這樣 Impala 查詢能夠評估分區鍵列中不一樣值的個數從而分佈工做負載

Parquet 數據文件的 Snappy 和 GZip 壓縮

當 Impala 使用 INSERT 語句寫入 Parquet 數據文件時,底層的壓縮受 PARQUET_COMPRESSION_CODEC 查詢選項控制。這一查詢選項容許的值包括 snappy (默認值), gzip, 和 none。選項值不區分大小寫。假如選項值設置爲一個未確認的值,由於無效的選項值,全部查詢都將失敗,不只僅是涉及到 Parquet 表的查詢。

使用 Snappy 壓縮的 Parquet 表

默認的,Parquet 表底層的數據文件採用 Snappy 壓縮。快速壓縮和解壓的組合使得對於許多數據集來講這是一個好選擇。爲了確保使用了 Snappy 壓縮,例如試驗了其餘壓縮編解碼以後,請在插入數據以前設置 PARQUET_COMPRESSION_CODEC 查詢選項爲 snappy:

[localhost:21000] > create database parquet_compression;
[localhost:21000] > use parquet_compression;
[localhost:21000] > create table parquet_snappy like raw_text_data;
[localhost:21000] > set PARQUET_COMPRESSION_CODEC=snappy;
[localhost:21000] > insert into parquet_snappy select * from raw_text_data;
Inserted 1000000000 rows in 181.98s

使用 GZip 壓縮的 Parquet 表

假如你須要更深刻的(more intensive)壓縮(當查詢時須要更多的 CPU 週期以進行解壓),請在插入數據以前設置 PARQUET_COMPRESSION_CODEC 查詢選項爲 gzip :

[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%):

$ hdfs dfs -du -h /user/hive/warehouse/parquet_compression.db
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)。

同時,更小的壓縮比,那麼解壓速度就更快。在上面包含 10 億行記錄的表中,對於評估特定列全部值的查詢,不使用壓縮比使用 Snappy 壓縮的快,使用 Snappy 壓縮的比使用 Gzip 壓縮的快。查詢性能依賴於幾個不一樣的因素,因此請一如既往的使用你本身的數據進行本身的基準測試,以得到數據大小、CPU 效率、以及插入和查詢操做的速度等方面理想的平衡。

[localhost:21000] > desc parquet_snappy;
Query finished, fetching results ...
+-----------+---------+---------+
| name      | type    | comment |
+-----------+---------+---------+
| id        | int     |         |
| val       | int     |         |
| zfill     | string  |         |
| name      | string  |         |
| assertion | boolean |         |
+-----------+---------+---------+
Returned 5 row(s) in 0.14s
[localhost:21000] > select avg(val) from parquet_snappy;
Query finished, fetching results ...
+-----------------+
| _c0             |
+-----------------+
| 250000.93577915 |
+-----------------+
Returned 1 row(s) in 4.29s
[localhost:21000] > select avg(val) from parquet_gzip;
Query finished, fetching results ...
+-----------------+
| _c0             |
+-----------------+
| 250000.93577915 |
+-----------------+
Returned 1 row(s) in 6.97s
[localhost:21000] > select avg(val) from parquet_none;
Query finished, fetching results ...
+-----------------+
| _c0             |
+-----------------+
| 250000.93577915 |
+-----------------+
Returned 1 row(s) in 3.67s

複製 Parquet 數據文件


下面是最後一個例子,演示了使用不一樣壓縮編解碼器的數據文件在讀操做上是如何相互兼容的。關於壓縮格式的元數據會寫入到每一個數據文件中,而且在讀取時無論當時 PARQUET_COMPRESSION_CODEC 設置爲何值,均可以正常解碼。在這個例子中,咱們從以前例子中使用的 PARQUET_SNAPPY,PARQUET_GZIP, PARQUET_NONE 表中複製數據文件,這幾個表中每個表都包含 10 億行記錄, 全都複製到新表 PARQUET_EVERYTHING 的數據目錄中。一對簡單的查詢展現了新表如今包含了使用不一樣壓縮編解碼器的數據文件的 30 億的記錄。

首先,咱們在 Impala 中建立表以便在 HDFS 中有一個存放數據文件的目標目錄:

[localhost:21000] > create table parquet_everything like parquet_snappy;
Query: create table parquet_everything like parquet_snappy

而後在 shell 中,咱們複製對應的數據文件到新表的數據目錄中。不採用 hdfs dfs -cp 這一一般複製文件的方式,咱們使用 hdfs distcp -pb 命令以確保 Parquet 數據文件特有的 1GB 塊大小繼續保留。

$ hdfs distcp -pb /user/hive/warehouse/parquet_compression.db/parquet_snappy \
  /user/hive/warehouse/parquet_compression.db/parquet_everything
...MapReduce output...
$ hdfs distcp -pb /user/hive/warehouse/parquet_compression.db/parquet_gzip  \
  /user/hive/warehouse/parquet_compression.db/parquet_everything
...MapReduce output...
$ hdfs distcp -pb /user/hive/warehouse/parquet_compression.db/parquet_none  \
  /user/hive/warehouse/parquet_compression.db/parquet_everything
...MapReduce output...

回到 impala-shell,咱們使用 REFRESH 語句讓 Impala 服務器識別到表中新的數據文件,而後咱們能夠運行查詢展現數據文件包含 30 億條記錄,而且其中一個數值列的值與原來小表的匹配:

[localhost:21000] > refresh parquet_everything;
Query finished, fetching results ...

Returned 0 row(s) in 0.32s
[localhost:21000] > select count(*) from parquet_everything;
Query finished, fetching results ...
+------------+
| _c0        |
+------------+
| 3000000000 |
+------------+
Returned 1 row(s) in 8.18s
[localhost:21000] > select avg(val) from parquet_everything;
Query finished, fetching results ...
+-----------------+
| _c0             |
+-----------------+
| 250000.93577915 |
+-----------------+
Returned 1 row(s) in 13.35s


與其餘 Hadoop 組件交流 Parquet 數據文件

自 CDH 4.5 開始,你能夠在 Hive、Pig、MapReduce 中讀取和寫入 Parquet 數據文件。參考 CDH 4 Installation Guide 瞭解詳細信息。

以前,不支持在 Impala 中建立 Parquet 數據而後在 Hive 中重用這個表。如今從 CDH 4.5 中的 Hive 開始支持 Parquet,在 Hive 中重用已有的 Impala Parquet 數據文件須要更新表的元數據。假如你已經使用 Impala 1.1.1 或更高版本,請使用下面的命令:

ALTER TABLE table_name SET FILEFORMAT PARQUET;

假如你使用比 Impala 1.1.1 更老的版本,經過 Hive 執行元數據的更新:

ALTER TABLE table_name SET SERDE 'parquet.hive.serde.ParquetHiveSerDe';
ALTER TABLE table_name SET FILEFORMAT
  INPUTFORMAT "parquet.hive.DeprecatedParquetInputFormat"
  OUTPUTFORMAT "parquet.hive.DeprecatedParquetOutputFormat";

Impala 1.1.1 及以上版本能夠重用 Hive 中建立的 Parquet 數據文件,不須要執行任何操做。

Impala 支持你能夠編碼成 Parquet 數據文件的標量數據類型,但不支持複合(composite)或嵌套(nested)類型如 maps/arrays。假如表中使用了任意不支持的類型,Impala 將沒法訪問這個表。

假如你在不一樣節點、乃至在相同節點的不一樣目錄複製 Parquet 數據文件,請使用 hadoop distcp -pb 命令以確保保留原有的塊大小。請執行 hdfs fsck -blocks HDFS_path_of_impala_table_dir 並檢查平均塊大小是否接近 1GB,以驗證是否保留了塊大小(Hadoop distcp 操做一般會生出一些子目錄,名稱爲 _distcp_logs_*,你能夠從目標目錄中刪除這些目錄)。參見 Hadoop DistCP Guide 瞭解詳細信息。

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 子句中列出,在同一個數據文件中同一行的全部列的數據均可用。

假如一個 INSERT 語句帶來少於 1GB 的數據,結果的數據文件小於理想大小。所以, 如何你是把一個 ETL 做業拆分紅多個 INSERT 語句,請儘可能保障每個 INSERT 語句插入的數據接近 1GB 或 1GB 的倍數。

Parquet 數據文件的行程編碼(RLE)和字典編碼

Parquet 使用一些自動壓縮技術,例如行程編碼(run-length encoding,RLE) 和字典編碼(dictionary encoding),基於實際數據值的分析。一當數據值被編碼成緊湊的格式,使用壓縮算法,編碼的數據可能會被進一步壓縮。Impala 建立的 Parquet 數據文件可使用 Snappy, GZip, 或不進行壓縮;Parquet 規格還支持 LZO 壓縮,可是目前 Impala 不支持 LZO 壓縮的 Parquet 文件。

除了應用到整個數據文件的 Snappy 或 GZip 壓縮以外,RLE 和字段編碼是 Impala 自動應用到 Parquet 數據值羣體的壓縮技術。這些自動優化能夠節省你的時間和傳統數據倉庫一般須要的規劃。例如,字典編碼減小了建立數字 IDs 做爲長字符串的縮寫的需求。

行程編碼(RLE)壓縮了一組重複數據值。例如,假如許多連續的行具備相同的國家編碼,這些重複的值能夠表示爲值和緊跟其後的值連續出現的次數。

字典編碼取出存在於列中的不一樣的值,每個值都表示爲一個 2 字節的組合而不是使用原始的可能有多個字節的值(對這些壓實的值進行了壓縮,額外節省了空間)。當列的不一樣值的個數少於 2**16 (16,384)個時,使用這一類型的編碼。他不會對 BOOLEAN 類型的列使用,由於已經足夠短。TIMESTAMP 的列有時每行都有不一樣的值,這時候可能很快就超過 2**16 個不一樣值的限制。列的這一 2**16 的限制被重置爲每個數據文件內的限制,這樣若是幾個不一樣的數據文件每一個都包含 10,000 個不一樣的城市名,每個數據文件中的城市名列仍然可使用字典編碼來凝練。

相關文章
相關標籤/搜索