我們介紹了在Hive中組織數(shù)據(jù)的規(guī)則和方法。本節(jié)作為《Hadoop從入門到精通》專題的第四章第二節(jié),將主要介紹如何在Hive中進(jìn)行數(shù)據(jù)壓縮,有哪些可選的數(shù)據(jù)壓縮方法等內(nèi)容。數(shù)據(jù)壓縮是一種將數(shù)據(jù)簡(jiǎn)化為更緊湊形式的機(jī)制,以節(jié)省存儲(chǔ)空間并提高數(shù)據(jù)傳輸效率。
?通過(guò)數(shù)據(jù)壓縮實(shí)現(xiàn)高效存儲(chǔ)
數(shù)據(jù)壓縮是文件處理的重要方面,在處理Hadoop支持的數(shù)據(jù)大小時(shí),這一點(diǎn)變得更加重要。大部分企業(yè)在使用Hadoop時(shí),目標(biāo)都是盡可能高效得進(jìn)行數(shù)據(jù)處理,選擇合適的壓縮編解碼器將使作業(yè)運(yùn)行更快,并允許在集群中存儲(chǔ)更多數(shù)據(jù)。
為數(shù)據(jù)選擇正確的壓縮編解碼器
在HDFS上使用壓縮并不像在ZFS等文件系統(tǒng)上那樣透明,特別是在處理可拆分的壓縮文件時(shí)(本章稍后將詳細(xì)介紹)。使用Avro和SequenceFile等文件格式的優(yōu)點(diǎn)是內(nèi)置壓縮支持,使壓縮幾乎對(duì)用戶完全透明。但是在使用文本等格式時(shí),就會(huì)失去這種支持。
問(wèn)題
評(píng)估并確定用于數(shù)據(jù)壓縮的最佳編解碼器。
解決方案
谷歌的壓縮編解碼器Snappy提供壓縮大小和讀/寫執(zhí)行時(shí)間的最佳組合。但是,當(dāng)使用必須支持可拆分性的大型壓縮文件時(shí),LZOP是最好的編解碼器。
討論
首先,快速瀏覽可用于Hadoop的壓縮編解碼器,如表4.1所示。
表4.1壓縮編解碼器
要正確評(píng)估編解碼器,首先需要確定評(píng)估標(biāo)準(zhǔn),該標(biāo)準(zhǔn)應(yīng)基于功能和性能特征。對(duì)于壓縮,你的標(biāo)準(zhǔn)可能包括以下內(nèi)容:
空間/時(shí)間權(quán)衡——通常,計(jì)算成本越高的壓縮編解碼器可以產(chǎn)生更好的壓縮比,從而產(chǎn)生更小的壓縮輸出。
可拆分性——可以拆分壓縮文件以供多個(gè)mapper使用。如果無(wú)法拆分壓縮文件,則只能使用一個(gè)mapper。如果該文件跨越多個(gè)塊,則會(huì)丟失數(shù)據(jù)局部性,因?yàn)閙ap可能必須從遠(yuǎn)程DataNode讀取塊,從而導(dǎo)致網(wǎng)絡(luò)I/O開銷。
本機(jī)壓縮支持——是否存在執(zhí)行壓縮和解壓縮的本地庫(kù)?這通常勝過(guò)用Java編寫的壓縮編解碼器,沒(méi)有底層的本機(jī)庫(kù)支持。
表4.2 壓縮編解碼器比較
Native vs Java bzip2
Hadoop添加了對(duì)bzip2的原生支持(從版本2.0和1.1.0開始)。本機(jī)bzip2支持是默認(rèn)的,但不支持可拆分性。如果需要可拆分性,就需要啟用Java bzip2,可以通過(guò)將io.compression .codec.bzip2.library設(shè)置為java-builtin來(lái)指定。
接下來(lái),我們來(lái)了解編解碼器在空間和時(shí)間上是如何平衡的。此處使用100 MB(10 ^ 8)的XML文件(來(lái)自http://mattmahoney.net/dc/textdata.html的enwik8.zip)來(lái)比較編解碼器運(yùn)行時(shí)間及其壓縮大小,具體測(cè)試結(jié)果見表4.3。
表4.3 100 MB文本文件上壓縮編解碼器的性能比較
運(yùn)行測(cè)試
當(dāng)進(jìn)行評(píng)估時(shí),我建議使用自己的數(shù)據(jù)進(jìn)行測(cè)試,最好是在類似于生產(chǎn)節(jié)點(diǎn)的主機(jī)上執(zhí)行測(cè)試,這樣就可以很好地理解編解碼器的預(yù)期壓縮和運(yùn)行時(shí)間。
要確保集群已啟用本機(jī)編解碼器,你可以通過(guò)運(yùn)行以下命令來(lái)檢查:
$ hadoop checknative -a
空間和時(shí)間的結(jié)果說(shuō)明了什么?如果將盡可能多的數(shù)據(jù)壓入集群是首要任務(wù),并且允許較長(zhǎng)的壓縮時(shí)間,那么bzip2可能是適合的編解碼器。如果要壓縮數(shù)據(jù)但要求在讀取和寫入壓縮文件時(shí)引入最少的CPU開銷,則應(yīng)該考慮LZ4。任何尋求壓縮和執(zhí)行時(shí)間之間平衡的企業(yè)都不會(huì)考慮bzip2的Java版本。
拆分壓縮文件很重要,但必須在bzip2和LZOP之間進(jìn)行選擇。原生bzip2編解碼器不支持拆分,Java bzip2 time可能會(huì)讓大多數(shù)人放棄。bzip2優(yōu)于LZOP的唯一優(yōu)勢(shì)是其Hadoop集成比LZOP更容易使用。
圖4.4 單個(gè)100 MB文本文件的壓縮大?。ㄝ^小的值更好)
圖4.5單個(gè)100 MB文本文件的壓縮和解壓縮時(shí)間(較小的值更好)
雖然LZOP似乎看起來(lái)是最優(yōu)的選擇,但還是需要做一些改進(jìn),正如下文所述。
總結(jié)
最適合的編解碼器取決于你的需求和標(biāo)準(zhǔn)。如果不關(guān)心拆分文件,LZ4是最有前途的編解碼器,如果想要拆分文件,LZOP就是最應(yīng)該關(guān)注的。
此外,我們還需要考慮數(shù)據(jù)是否需要長(zhǎng)期存儲(chǔ)。如果長(zhǎng)時(shí)間保存數(shù)據(jù),你可能希望最大限度地壓縮文件,我建議使用基于zlib的編解碼器(例如gzip)。但是,由于gzip不可拆分,因此將它與基于塊的文件格式(如Avro或Parquet)結(jié)合使用是明智的,這樣數(shù)據(jù)仍然可以拆分,或者調(diào)整輸出大小使其在HDFS中占用一個(gè)塊,這樣就不需要考慮是否可拆分。
請(qǐng)記住,壓縮大小將根據(jù)文件是文本還是二進(jìn)制而有所不同,具體取決于其內(nèi)容。要獲得準(zhǔn)確的數(shù)字,需要針對(duì)自己的數(shù)據(jù)運(yùn)行類似的測(cè)試。
對(duì)HDFS中的數(shù)據(jù)進(jìn)行壓縮有許多好處,包括減小文件大小和更快的MapReduce作業(yè)運(yùn)行時(shí)。許多壓縮編解碼器可用于Hadoop,我根據(jù)功能和性能對(duì)它們進(jìn)行了評(píng)估。接下來(lái),讓我們看看如何壓縮文件并通過(guò)MapReduce,Pig和Hive等工具使用它們。
使用HDFS,MapReduce,Pig和Hive進(jìn)行壓縮
由于HDFS不提供內(nèi)置的壓縮支持,因此在Hadoop中使用壓縮可能是一項(xiàng)挑戰(zhàn)。此外,可拆分壓縮不適合技術(shù)水平不高的初學(xué)者,因?yàn)樗⒉皇荋adoop開箱即用的功能。如果正在處理壓縮到接近HDFS塊大小的中型文件,以下方法將是在Hadoop中壓縮優(yōu)勢(shì)最明顯和最簡(jiǎn)單的方法。
問(wèn)題
希望在HDFS中讀取和寫入壓縮文件,并將其與MapReduce,Pig和Hive一起使用。
解決方案
在MapReduce中使用壓縮文件涉及更新MapReduce配置文件mapred-site.xml并注冊(cè)正在使用的壓縮編解碼器。執(zhí)行此操作后,在MapReduce中使用壓縮輸入文件不需要額外的步驟,并且生成壓縮的MapReduce輸出是設(shè)置mapred.output.compress和mapred.output.compression.codec MapReduce屬性的問(wèn)題。
討論
第一步是弄清楚如何使用本章前面評(píng)估的編解碼器來(lái)讀取和寫入文件。本章詳細(xì)介紹的所有編解碼器都與Hadoop捆綁在一起,但LZO / LZOP和Snappy除外,如果想使用這三種編解碼器,需要自己下載并構(gòu)建。
要使用壓縮編解碼器,首先需要知道它們的類名,如表4.4所示。
表4.4 編解碼器類
在HDFS中使用壓縮
如何使用上表中提到的任何一種編解碼器壓縮HDFS中的現(xiàn)有文件?以下代碼支持這樣做:
編解碼器緩存使用壓縮編解碼器的一個(gè)開銷是創(chuàng)建成本很高。當(dāng)使用Hadoop ReflectionUtils類時(shí),與創(chuàng)建實(shí)例相關(guān)的一些開銷將緩存在ReflectionUtils中,這將加速后續(xù)創(chuàng)建編解碼器。更好的選擇是使用CompressionCodecFactory,它本身提供編解碼器緩存。
讀取此壓縮文件就像編寫一樣簡(jiǎn)單:
超級(jí)簡(jiǎn)單。既然可以創(chuàng)建壓縮文件,那么讓我們看看如何在MapReduce中使用。
在MapReduce中使用壓縮
要在MapReduce中使用壓縮文件,需要為作業(yè)設(shè)置一些配置選項(xiàng)。為簡(jiǎn)潔起見,我們假設(shè)在此示例中使用了identity mapper和reducer:
使用未壓縮I/O與壓縮I/O的MapReduce作業(yè)之間的唯一區(qū)別是前面示例中的三個(gè)帶注釋的行。
不僅可以壓縮作業(yè)的輸入和輸出,而且中間map輸出也可以壓縮,因?yàn)樗紫容敵龅酱疟P,最終通過(guò)網(wǎng)絡(luò)輸出到reducer。map輸出的壓縮有效性最終取決于發(fā)出的數(shù)據(jù)類型,但一般情況下,我們可以通過(guò)進(jìn)行此更改來(lái)加速某些作業(yè)進(jìn)程。
為什么不必在前面的代碼中為輸入文件指定壓縮編解碼器?默認(rèn)情況下,F(xiàn)ileInputFormat類使用CompressionCodecFactory來(lái)確定輸入文件擴(kuò)展名是否與已注冊(cè)的編解碼器匹配。如果找到與該文件擴(kuò)展名相關(guān)聯(lián)的編解碼器,會(huì)自動(dòng)使用該編解碼器解壓縮輸入文件。
MapReduce如何知道要使用哪些編解碼器?需要在mapred-site.xml中指定編解碼器。 以下代碼顯示了如何注冊(cè)上述提到的所有編解碼器。請(qǐng)記住,除了gzip,Deflate和bzip2之外,所有壓縮編解碼器都需要構(gòu)建并在集群上可用,然后才能注冊(cè):
現(xiàn)在,你已經(jīng)使用MapReduce掌握了壓縮,是時(shí)候了解Hadoop堆棧信息了。因?yàn)閴嚎s也可以與Pig和Hive一起使用,讓我們看看如何使用Pig和Hive鏡像完成MapReduce壓縮。
如果你正在使用Pig,那么使用壓縮輸入文件不需要額外的工作,需要做的就是確保文件擴(kuò)展名map到相應(yīng)的壓縮編解碼器(參見表4.4)。以下示例是gzips本地加密文件加載到Pig,并轉(zhuǎn)儲(chǔ)用戶名的過(guò)程:
寫gzip壓縮文件是一樣的,都要確保指定壓縮編解碼器的擴(kuò)展名。以下示例將Pig關(guān)系B的結(jié)果存儲(chǔ)在HDFS文件中,然后將它們復(fù)制到本地文件系統(tǒng)以檢查內(nèi)容:
在Hive中使用壓縮
與Pig一樣,我們需要做的就是在定義文件名時(shí)指定編解碼器擴(kuò)展:
前面的示例將一個(gè)gzip壓縮文件加載到Hive中。在這種情況下,Hive將正在加載的文件移動(dòng)到數(shù)據(jù)倉(cāng)庫(kù)目錄,并繼續(xù)使用原始文件作為表的存儲(chǔ)。
如果要?jiǎng)?chuàng)建另一個(gè)表并指定需要被壓縮該怎么辦?下面的示例通過(guò)一些Hive配置來(lái)啟用MapReduce壓縮實(shí)現(xiàn)這一點(diǎn)(因?yàn)閷?zhí)行MapReduce作業(yè)以在最后一個(gè)語(yǔ)句中加載新表):
我們可以通過(guò)在HDFS中查看來(lái)驗(yàn)證Hive是否確實(shí)壓縮了新apachelog_backup表的存儲(chǔ):
應(yīng)該注意的是,Hive建議使用SequenceFile作為表的輸出格式,因?yàn)镾equenceFile塊可以單獨(dú)壓縮。
總結(jié)
此技術(shù)提供了一種在Hadoop中運(yùn)行壓縮的快速簡(jiǎn)便方法,這適用于不太大的文件,因?yàn)樗峁┝艘环N相對(duì)透明的壓縮方式。如果壓縮文件遠(yuǎn)大于HDFS塊大小,請(qǐng)考慮以下方法。
可拆分LZOP,帶有MapReduce,Hive和Pig
如果你正在使用大型文本文件,即使在壓縮時(shí),這也會(huì)比HDFS塊大小大很多倍。為避免讓一個(gè)map任務(wù)處理整個(gè)大型壓縮文件,你需要選擇一個(gè)可支持拆分該文件的壓縮編解碼器。
LZOP符合要求,但使用它比上文示例更復(fù)雜,因?yàn)長(zhǎng)ZOP本身不可拆分。因?yàn)長(zhǎng)ZOP是基于塊的,不可能隨機(jī)搜索LZOP文件并確定下一個(gè)塊的起點(diǎn),這是該方法面臨的挑戰(zhàn)。
問(wèn)題
希望使用壓縮編解碼器,以允許MapReduce在單個(gè)壓縮文件上并行工作。
解決方案
在MapReduce中,拆分大型LZOP壓縮輸入文件需要使用LZOP特定的輸入格式類,例如LzoInputFormat。在Pig和Hive中使用LZOP壓縮的輸入文件時(shí),同樣的原則也適用。
討論
LZOP壓縮編解碼器是僅有的允許拆分壓縮文件的兩個(gè)編解碼器之一,因此多個(gè)Reducer可并行處理。另一個(gè)編解碼器bzip2受到壓縮時(shí)間的影響導(dǎo)致運(yùn)行很慢,可能會(huì)導(dǎo)致編解碼器無(wú)法使用,LZOP提供了壓縮和速度之間的良好權(quán)衡。
LZO和LZOP有什么區(qū)別?LZO和LZOP編解碼器都可用于Hadoop。LZO是一個(gè)基于流的壓縮存儲(chǔ),沒(méi)有塊或頭的概念。LZOP具有塊(已校驗(yàn)和)的概念,因此是要使用的編解碼器,尤其是在希望壓縮輸出可拆分的情況下。令人困惑的是,Hadoop編解碼器默認(rèn)情況下將以.lzo擴(kuò)展名結(jié)尾的文件處理為L(zhǎng)ZOP編碼,以.lzo_deflate擴(kuò)展名結(jié)尾的文件處理為L(zhǎng)ZO編碼。此外,許多文檔似乎可以互換使用LZO和LZOP。
不幸的是,由于許可原因,Hadoop并未不捆綁LZOP。在集群上編譯和安裝LZOP非常費(fèi)力,要編譯本文代碼,還請(qǐng)先行安裝配置LZOP。
在HDFS中讀寫LZOP文件
如果要使用LZOP讀寫壓縮文件,我們需要在代碼中指定LZOP編解碼器:
代碼4.3在HDFS中讀寫LZOP文件的方法
讓我們編寫并讀取LZOP文件,確保LZOP實(shí)用程序可以使用生成的文件(將$ HADOOP_CONF_HOME替換為Hadoop配置目錄的位置):
以上代碼將在HDFS中生成core-site.xml.lzo文件。
現(xiàn)在確??梢詫⒋薒ZOP文件與lzop二進(jìn)制文件一起使用。在主機(jī)上安裝lzop二進(jìn)制文件將LZOP文件從HDFS復(fù)制到本地磁盤,使用本機(jī)lzop二進(jìn)制文件解壓縮,并將其與原始文件進(jìn)行比較:
diff驗(yàn)證了使用LZOP編解碼器壓縮的文件可以使用lzop二進(jìn)制文件解壓縮。
既然已經(jīng)擁有了LZOP文件,我們需要對(duì)其進(jìn)行索引以便可以拆分。
為L(zhǎng)ZOP文件創(chuàng)建索引
LZOP文件本身不可拆分,雖然其具有塊的概念,但缺少塊分隔同步標(biāo)記意味著無(wú)法隨機(jī)搜索LZOP文件并開始讀取。但是因?yàn)樵趦?nèi)部確實(shí)使用了塊,所以只需要做一些預(yù)處理即可,它可以生成一個(gè)包含塊偏移的索引文件。
完整讀取LZOP文件,并在讀取發(fā)生時(shí)將塊偏移寫入索引文件。索引文件格式(如圖4.6所示)是一個(gè)二進(jìn)制文件,包含一系列連續(xù)的64-bit數(shù)字,表示LZOP文件中每個(gè)塊的字節(jié)偏移量。
你可以使用以下兩種方式創(chuàng)建索引文件,如果要為單個(gè)LZOP文件創(chuàng)建索引文件,只需要進(jìn)行一個(gè)簡(jiǎn)單的庫(kù)調(diào)用即可,如下:
shell$ hadoop com.hadoop.compression.lzo.LzoIndexer core-site.xml.lzo
如果有大量LZOP文件并且需要更有效的方法來(lái)生成索引文件,索引器運(yùn)行MapReduce作業(yè)以創(chuàng)建索引文件,支持文件和目錄(以遞歸方式掃描LZOP文件):
圖4.6中描述的兩種方法都將在與LZOP文件相同的目錄中生成索引文件。索引文件名是以.index為后綴的原始LZOP文件名。運(yùn)行以前的命令將生成文件名core-site.xml.lzo.index。
接下來(lái),我們來(lái)看看如何在Java代碼中使用LzoIndexer。以下代碼(來(lái)自LzoIndexer的主方法)將導(dǎo)致同步創(chuàng)建索引文件:
使用DistributedLzoIndexer,MapReduce作業(yè)將啟動(dòng)并運(yùn)行N個(gè)mapper,每個(gè).lzo文件一個(gè)。沒(méi)有運(yùn)行reducer,因此(identity)mapper通過(guò)自定義LzoSplitInputFormat和LzoIndexOutputFormat直接寫入索引文件。
如果要從自己的Java代碼運(yùn)行MapReduce作業(yè),可以使用DistributedLzoIndexer代碼。
需要LZOP索引文件,以便可以在MapReduce,Pig和Hive作業(yè)中拆分LZOP文件。既然已經(jīng)擁有了上述LZOP索引文件,讓我們看一下如何將它們與MapReduce一起使用。
MapReduce和LZOP
在為L(zhǎng)ZOP文件創(chuàng)建索引文件之后,就可以開始將LZOP文件與MapReduce一起使用了。不幸的是,這給我們帶來(lái)了下一個(gè)挑戰(zhàn):現(xiàn)有的基于Hadoop文件的內(nèi)置輸入格式都不適用于可拆分LZOP,因?yàn)樗鼈冃枰獙iT的邏輯來(lái)處理使用LZOP索引文件的輸入拆分。我們需要特定的輸入格式類才能使用可拆分LZOP。
LZOP庫(kù)為面向行的LZOP壓縮文本文件提供了LzoTextInputFormat實(shí)現(xiàn),并附帶索引文件。
以下代碼顯示了配置MapReduce作業(yè)以使用LZOP所需的步驟。 我們將對(duì)具有文本LZOP輸入和輸出的MapReduce作業(yè)執(zhí)行以下步驟:
壓縮中間map輸出還將減少M(fèi)apReduce作業(yè)的總體執(zhí)行時(shí)間:
可以通過(guò)編輯hdfs-site.xml輕松配置集群以始終壓縮map輸出:
每個(gè)LZOP文件的拆分?jǐn)?shù)量是文件占用的LZOP塊數(shù)量的函數(shù),而不是文件占用的HDFS塊數(shù)量函數(shù)。
Pig和Hive
Elephant Bird,一個(gè)包含與LZOP一起工作的實(shí)用程序的Twitter項(xiàng)目,提供了許多有用的MapReduce和Pig類。Elephant Bird有一個(gè)LzoPigStorage類,可以在Pig中使用基于文本的LZOP壓縮數(shù)據(jù)。
通過(guò)使用LZO庫(kù)中的com.hadoop.mapred .DeprecatedLzoTextInputFormat輸入格式類,Hive可以使用LZOP壓縮的文本文件。
總結(jié)
在Hadoop中使用可拆分壓縮是很棘手的。如果有幸能夠?qū)?shù)據(jù)存儲(chǔ)在Avro或Parquet中就會(huì)省心不少,因?yàn)樗鼈兲峁┝俗詈?jiǎn)單的方法來(lái)處理可輕松壓縮和拆分的文件。如果想壓縮其他文件格式并進(jìn)行拆分,LZOP是唯一的候選者。
正如之前提到的,Elephant Bird項(xiàng)目提供了一些有用的LZOP輸入格式,可以使用LZOP壓縮文件格式,如XML和純文本。如果需要使用Todd Lipcon的LZO項(xiàng)目或Elephant Bird不支持的LZOP壓縮文件格式,則必須編寫自己的輸入格式。這對(duì)開發(fā)人員來(lái)說(shuō)是一個(gè)很大的挑戰(zhàn)?,F(xiàn)在,不少開發(fā)者都對(duì)Hadoop進(jìn)行了改進(jìn),使其能夠支持具有自定義拆分邏輯的壓縮文件,最終用戶不必編寫自己的壓縮輸入格式。
對(duì)于資源總是稀缺的生產(chǎn)環(huán)境而言,壓縮可能是一項(xiàng)很艱巨的任務(wù)。壓縮還允許更快的作業(yè)執(zhí)行時(shí)間,因此它是存儲(chǔ)的一個(gè)引人注目的方面。本節(jié)主要介紹了評(píng)估和選擇最適合數(shù)據(jù)的編解碼器,以及如何使用HDFS,MapReduce,Pig和Hive進(jìn)行壓縮等內(nèi)容,希望對(duì)各位有所幫助。
評(píng)論
查看更多