1. 程式人生 > >【Hive】11-其他檔案格式和壓縮方法

【Hive】11-其他檔案格式和壓縮方法

Hive的一個獨特的功能就是:Hive不會強制要求將資料轉換成特定的格式才能使用。
Hive利用Hadoop的InputFormatAPI來從不同的資料來源讀取資料,例如文字格式、sequence檔案格式,甚至使用者自定義格地。同樣地,使用OutputFormat API也可以將資料寫成不同的格式。
儘管Hadoop的檔案系統支援對於非壓縮資料的線性擴充套件儲存,但是對資料進行壓縮還是有很大好處的。壓縮通常都會節約可觀的磁碟空間,例如,基於文字的檔案可以壓縮4儼甚至更高比例。壓縮同樣可以增加吞吐量和效能。這看上去似乎並不合常理,因為壓縮和解壓縮會增加額外的CPU開銷,不過,通過減少載人記憶體的資料量而提高I/O吞吐量會更加提高網路傳輸效能。
Hadoop的job通常是I/O密集型而不是CPU密集型的。如果是這樣的話,壓縮可以提高效能。不過,如果使用者的job是CPU密集型的話,那麼使用壓縮可能會降低執行效能。確定是否進行壓縮的唯一方法就是嘗試不同的選擇,並測量對比執行結果。

1、確定安裝編解碼器

基於使用者所使用的 Hadoop 版本,會提供不同的編解碼器。 Hive 中可以通過 set 命令檢視 Hive 配皿檔案中或 Hadoop 配裡檔案中配裡的值。通過檢視屬性 io.compression.codec ,可以看到編解碼器之間是按照逗號進行分割的:

2、選擇一種壓縮編/解碼器

使用壓縮的優勢是可以最小化所需要的磁碟儲存空間,以及減小磁碟和網路IO 操作。不過,檔案壓縮過程和解壓縮過程會增加 CPU 開銷。因此,對於壓縮密集型的 job 最好使用壓縮,特別是有額外的 CPU 資源或磁碟儲存空間比較稀缺的情況。所有最新的那些 Hadoop 版本都已經內建支援 GZIP 和 BZip2 壓縮方案了,包括加速對這些格式的壓縮和解壓縮的本地 Liunx 庫。繫結支援 SnapPy 壓縮是最近才增加的,不過,如果使用者當前使用的 Hadoop 版本不支援該功能的話,那麼自行增加相關的庫即可。另外,還有一種常用的壓縮方案,即 LZO 壓縮。

那麼,為什麼我們需要不同的壓縮方案呢?每一個壓縮方案都在壓縮/解壓縮速度和壓縮率間進行權衡。 BZip2 壓縮率最高,但是同時擂要消耗最多的 CPU 開銷。 Gzip 是壓縮率和壓縮/解壓縮速度上的下一個選擇。因此,如果磁碟空間利用率和 IO 開銷都需要考慮的話,那麼這 2 種壓縮方案都是有吸引力的。 LZO 和 Snappy 壓縮率相比前面的 2 種要小但是壓縮/解壓縮速度要快,特別是解壓縮過程。如果相對於磁碟空間和 IO 開銷,頻繁讀取資料所有的解壓縮速度更重要的話,那麼它們將是不錯的選擇。

另一個而要考慮的因素是壓縮格式的檔案是否是可分割的。 MapReduce許要將非常大的輸人檔案分割成多個劃分(通常一個檔案塊對應一個劃分,也就是 128MB 的倍數) , 其中每個劃分會被分發到一個單獨的 map 程序中。只有當 Hadoop 知道檔案中記錄的邊界時才可以進行這樣的分割。對於文字檔案,每一行都是一條記錄,但是 Gzip 和 Snappy 將這些邊界資訊掩蓋掉了。不過, Bzip2 和 LZO 提供了塊( BLOCK)級別的壓縮,也就是每個塊中都含有完整的記錄資訊,因此 Hadoop 可以在塊邊界級別對這些檔案進行劃分。

雖然 Gzip 和 Snappy 壓縮的檔案不可劃分,但是並不能因此而排除它們.當用戶建立檔案的時候,可以將檔案分割成期望的檔案大小。通常輸出檔案的個數等於 reducer 的個數。也就是說如果使用者使用了 N 個 reducer ,那麼通常就會得到 N 個輸出檔案。需要注意的是,如果有一個不可分割的檔案特別的大,那麼就會出現一個單獨的 task 來讀取整個檔案,進行處理。

從 Hive的角度來看,對於檔案格式,有 2 個方面的內容需要說明。一個方面是檔案是怎樣分隔成行(記錄)的。文字檔案使用場(換行符)作為預設的行分隔符。當用戶沒有使用預設的文字檔案格式時,使用者需要告訴 Hive 使用的 InputFomat 和 outputFormat 是什麼。事實上,使用者播要指定對於輸人和輸出格式實現的 Java 類的名稱。 InPutFormat 中定義瞭如何讀取劃分,以及如何將劃分分割成記錄,而 OutPutFormat 中定義瞭如何將這些劃分寫回到檔案或控制檯輸出中。

第 2 個方面是記錄是如何分割成欄位(或列)的。 Hive 使用^A 作文字檔案中預設的欄位分隔符。 Hive 使用 SerDe(也就是序列俗反序列化的簡寫)作為對輸人記錄 (反序列化)進行分割以及寫記錄(序列化)的“模板”。這時,使用者只需要指定可以完成這 2 部分工作的一個 Java 類即可。

所有的這些資訊使用者在建立表的時候都可以在表定義語句中進行指定。建立完成後,使用者可以像平時一樣查詢表,而無需關心底層格式。因此,如果使用者是 Hive 的使用者,而不是 Java 工程師,就無需關心關於 Java 的資訊。如果需要,使用者所在團隊的工程師可以幫忙指定需要的定義資訊,而之後就可以像之前一樣工作了。

3、開啟中間壓縮

對中間資料進行壓縮可以減少 job 中 map和 reduce task 間的資料傳輸盤。對於中間資料壓縮,選擇一個低 CPU 開銷的編/解碼器要比選擇一個壓縮率高的編/解碼器要重要得多。屬性 hive.exec.compress.intermediate 的預設值是 false ,如果要開啟中間壓縮,就需要將這個屬性值修改為預設值為 true :

提示:對於其他 Hadoop job 來說控制中間資料壓編的屬性是 mapred.compress.map.output 。

Hadoop 壓縮預設的編/解碼器是 DefaultCodec .可以通過修改屬性 mapred.map.output.compression.codec 的值來修改編/解碼器。這是一個 Hadoop 配置項,可以在$HADOOP_HOME/conf/mapred-site.xml 檔案中或$HIVE_HOME/conf/hive-site.xml 檔案中進行配置。SnappyCodec 是一個比較好的中間檔案壓縮編/解碼器,因為其很好地結合了低 CPU 開銷和好的壓縮執行效率:

4、最終輸出結果壓縮

當 Hive 將輸出寫人到表中時,輸出內容同樣可以進行壓縮。屬性 hive.exec.compress.output 控制著這個功能。使用者可能需要保持預設配置檔案中的預設值 false , 這樣預設的輸出就是非壓縮的純文字檔案了。使用者可以通過在查詢語句或執行指令碼中設定這個值為true ,來開啟輸出結果壓縮功能:

提示:對於其他 Hadoop 任務,開啟最終愉出結果壓縮功能的屬性是 mapred.output.compress。

如果屬性hive.exec.compress.output 的值設定為true,那麼這時需要為其指定一個編解碼器。對於輸出檔案,使用 Gzip 進行壓縮是個不錯的主意,因為其通常可以大幅度降低檔案的大小。但是,需要記住的是 Gzip 壓縮的檔案對於後面的 MapReduce job 而言是不可分割的。

5、Sequencefile 儲存格式

壓縮檔案確實能夠節約儲存空間,但是,在 Hadoop 中儲存裸壓縮檔案的一個缺點就是,通常這些檔案是不可分割的。可分割的檔案可以劃分成多個部分,由多個 mapper 並行進行處理。大多數的壓縮檔案是不可分割的。也就是說只能從頭讀到尾。 Hadoop 所支援的 sequence file 儲存格式可以將一個檔案劃分成多個塊,然後採用一種可分割的方式對塊進行壓縮。如果想在 Hivc 中使用sequence file 儲存格式,那麼需要在 CREATE TABLE 語句中通過 STORED AS SEQUENCEFILE語句進行指定:

CREATE TABLE a_sdequence_file_table STORED AS SEQUENCEFILE

Sequence file 提供3 種壓縮方式: NONE 、 RECORD 和 BLOCK ,預設是 RECORD 級別(也就是記錄級別)。不過,通常來說, BLOCK 級別(也就是塊級別)壓縮效能最好而且是可以分割的。和很多其他的壓縮屬性一樣,這個屬性也並非是 Hive 特有的。使用者可以在 Hadoop 的 mapred-site.xml 檔案中進行定義,或者在 Hive 的 hive-site.xml 檔案中進行定義,需要的時候,還可以在指令碼中或查詢語句前進行指定:

6、使用壓縮實踐

我們已經介紹了 Hive 中可以使用的一些壓縮相關的配置屬性,這些屬性的不同組合方式將會產生不同的輸出。下面,讓我們在一些例子中使用這些屬性,然後展示下它們的輸出將會是什麼。浦要注意的是, CLI 中通過對命令設定的屬性在同一個會話中會一直生效的。因此,同一個會話中的不同例子間應該注意將設裡復原,或者直接重開啟一個 Hive 會話:

首先,我們先開啟中間資料壓縮功能。這將不會影響到最終輸出結果.不過從 job 的計數器資訊上看,可以發現這個 job 中間傳送的資料裡變小了,因為 shuffle sort (混洗排序)資料被壓縮了:

和預期的一樣,中間資料壓縮沒有影響到最終的輸出,最終的資料結果仍然是非壓縮的:

我們同樣可以為中間資料壓縮配置其他的編/解碼器而不使用預設的編/解碼器。下面這個例子,我們選擇使用 Gzip (儘管通常 Snappy 是更好的選擇)。為顯示清晰,將下面語句中的第 1 行分成了 2 行:

下一步,我們可以開啟輸出結果:

輸出資訊中的表統計資訊顯示 total_size (總大小)是 16B ,但是 raw_data_size (裸資料)是 6B ,多出的儲存空間是被 deflate 演算法消耗了。同時我們可以看到,輸出的檔案字尾名是.deflate不推薦使用 cat 命令來查著這個壓縮檔案,因為使用者只能看到二進位制輸出。不過, Hive 可以正常地查詢這個資料:

這種無縫的處理壓縮檔案的能力並非是 Hive 獨有的,實際上,這裡是使用了 Hadoop 的 TextlnputFormat 進行的處理。儘管在這種情況下這個命名有些令人混淆,但是 TextlnputFormat 可以識別檔案字尾名為.deflate 或gz 的壓縮檔案,並且可以很輕鬆地進行處理。 Hive 無需關心底層的檔案是否是壓縮的,以及是使用何種壓縮方案進行壓縮的。下面我們改變下輸出結果壓縮所使用的編解碼器,然後看下結果(這裡語句的第 2 行為了顯示清晰,也分成 2 行了) :

正如使用者可以看到的,輸出資料夾下現在包含了零個或多個.gz檔案。 Hive 提供了在 Hive shell中執行像 zcat 這樣的本地命令的快速方法。通過!符號, Hive 可以執行外部的命令,直到返回結果。 zcat 是一個命令列實用程式,用來解壓縮,並進行輸出顯示:

使用這種翰出壓縮能夠完成那種很小.操作起來很快的檔案的二進位制壓縮。不過,回想一下,輸出檔案的個數是和處理資料所需要的 mapper 個數或 reducer 個數有關的。在最壞的情況下,可能最終結果是資料夾中只產生了一個大的壓縮檔案,而且是不可分割的。這意味著後續步驟並不能並行地處理這個資料。解決這個問題的答案就是使用 sequence file :

Sequence file是二進位制格式的,但是,我們可以很容易地查詢檔案頭。通過檢視檔案頭可以確認結果是否使我們需要的:

因為 Sequence file 中嵌人的元資料資訊以及 Hive 元資料資訊,無需任何特別的設定就可以查詢這張表。 Hadoop 同樣提供了 dfs -text 命令來從sequence file檔案中去掉檔案頭和壓縮,然後顯示裸資料: 

最後,我們來同時使用直接資料壓縮和最終輸出資料壓縮,而且使用不同壓縮編/解碼器的sequence file!這些設裡通常應用在生產環境,這些環境中的資料集很大,而這些設裡可以提高其效能:

7、存檔分割槽

Hadoop 中有一種儲存格式名為 HAR ,也就是 Hadoop Archive ( Hadoop 歸檔檔案)的簡寫。一個 HAR 檔案就像在 HDFS 檔案系統中的一個 TAR 檔案一樣是一個單獨的檔案。不過,其內部可以存放多個檔案和資料夾。在一些使用場景下,較舊的資料夾和檔案比較新的資料夾和檔案被訪問的概率要低很多。如果某個特定的分割槽下儲存的檔案有成千上萬的話,那麼就需要 HDFS 中的 NameNode 消耗非常大的代價來管理這些檔案。通過將分割槽下的檔案歸檔成一個巨大的,但是同時可以被 Hive 訪問的檔案,可以減輕 NameNode 的壓力。不過其缺點是, HAR 檔案查詢效率不高.同時, HAR 檔案並非是壓縮的.因此也不會節約儲存空間。

在下面的例子中,我們將使用 Hive 自帶的檔案作為表資料。首先,建立一個分割槽表,然後將 Hive 包中自帶的檔案載入到表中:

某些 Hadoop (例如 Hadoop v0.20.2 版本)要求包含 Hadoop 歸檔工具介面的 JAR包要放置在 Hive 的 auxlib 路徑下:

 

在歸檔之前,我們來看下這個表下面的底層目錄結構.需要注意的是表下面的資料是儲存在分割槽目錄下的,因為這是一個管理分割槽表:

ALTER TABLE … ARCHIVE PARTITION 語句將錶轉化成了一個歸檔表:

(對於上面的輸出,為便於展示,我們對其格式進行了調整,並使用 … 替換掉輸出中的時間戮。)下面這張表中由之前的兩個檔案變成了現在的一個 Hadoop 歸檔檔案(也就是 HAR 檔案) :

ALTER TABLE … UNARCHIVE PARTITION 命令可以將 HAR 中的檔案提取出來然後重新放置到 HDFS 中:

ALTER TABLE hive_text UNARCHIVE PARTITION (folder = 'docs' );

8、壓縮:包紮

Hive 可以讀和寫不同型別的壓縮檔案,確實可以獲取很大的效能提升,因為其可以節約磁儲存空間以及處理開銷。這種靈活性同樣也有助於和其他工具進行整合,因為 Hive 無裔使用 Java 編寫的自定義的“介面卡”就可以查詢很多本地檔案型別。