一文搞懂Hive的數(shù)據(jù)存儲(chǔ)與壓縮

行存儲(chǔ)與列存儲(chǔ)
行存儲(chǔ)的特點(diǎn)
列存儲(chǔ)的特點(diǎn)
常見的數(shù)據(jù)格式
TextFile
SequenceFile
RCfile
ORCfile
Parquet
測(cè)試
準(zhǔn)備測(cè)試數(shù)據(jù)
存儲(chǔ)空間大小
測(cè)試SQL 執(zhí)行效率
總結(jié)
Hive 壓縮
Hive中間數(shù)據(jù)壓縮
最終輸出結(jié)果壓縮
常見的壓縮格式
演示
總結(jié)
行存儲(chǔ)與列存儲(chǔ)
當(dāng)今的數(shù)據(jù)處理大致可分為兩大類,聯(lián)機(jī)事務(wù)處理 OLTP(on-line transaction processing)聯(lián)機(jī)分析處理 OLAP(On-Line Analytical Processing)=,OLTP 是傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)的主要應(yīng)用來執(zhí)行一些基本的、日常的事務(wù)處理比如數(shù)據(jù)庫(kù)記錄的增、刪、改、查等等而OLAP則是分布式數(shù)據(jù)庫(kù)的主要應(yīng)用它對(duì)實(shí)時(shí)性要求不高,但處理的數(shù)據(jù)量大通常應(yīng)用于復(fù)雜的動(dòng)態(tài)報(bào)表系統(tǒng)上

所以一般OLTP 都是使用行式存儲(chǔ)的,因?yàn)閷?shí)時(shí)性要求高,而且有大量的更新操作,OLAP 都是使用列式存儲(chǔ)的,因?yàn)閷?shí)時(shí)性要求不高,主要是要求性能好
行存儲(chǔ)的特點(diǎn)
查詢滿足條件的一整行數(shù)據(jù)的時(shí),只需要找到其中一個(gè)值,其余的值都在相鄰地方,所以此時(shí)行存儲(chǔ)查詢的速度更快。 傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù),如 Oracle、DB2、MySQL、SQL SERVER 等采用行式存儲(chǔ)法(Row-based),在基于行式存儲(chǔ)的數(shù)據(jù)庫(kù)中, 數(shù)據(jù)是按照行數(shù)據(jù)為基礎(chǔ)邏輯存儲(chǔ)單元進(jìn)行存儲(chǔ)的, 一行中的數(shù)據(jù)在存儲(chǔ)介質(zhì)中以連續(xù)存儲(chǔ)形式存在。 TEXTFILE和SEQUENCEFILE的存儲(chǔ)格式都是基于行存儲(chǔ)的 這種存儲(chǔ)格式比較方便進(jìn)行INSERT/UPDATE操作,不足之處就是如果查詢只涉及某幾個(gè)列,它會(huì)把整行數(shù)據(jù)都讀取出來,不能跳過不必要的列讀取。當(dāng)然數(shù)據(jù)比較少,一般沒啥問題,如果數(shù)據(jù)量比較大就比較影響性能,還有就是由于每一行中,列的數(shù)據(jù)類型不一致,導(dǎo)致不容易獲得一個(gè)極高的壓縮比,也就是空間利用率不高
列存儲(chǔ)的特點(diǎn)
查詢時(shí),只有涉及到的列才會(huì)被查詢,不會(huì)把所有列都查詢出來,即可以跳過不必要的列查詢,在查詢只需要少數(shù)幾個(gè)字段的時(shí)候,能大大減少讀取的數(shù)據(jù)量;因?yàn)槊恳涣械臄?shù)據(jù)都是存儲(chǔ)在一起的,每個(gè)字段的數(shù)據(jù)類型一定是相同的,列式存儲(chǔ)可以針對(duì)性的設(shè)計(jì)更好的設(shè)計(jì)壓縮算法,高效的壓縮率,不僅節(jié)省儲(chǔ)存空間也節(jié)省計(jì)算內(nèi)存和CPU
不足之處是INSERT/UPDATE很麻煩或者不方便,不適合掃描小量的數(shù)據(jù)
列式存儲(chǔ)(Column-based)是相對(duì)于行式存儲(chǔ)來說的,新興的Hbase、HPVertica、EMCGreenplum等分布式數(shù)據(jù)庫(kù)均采用列式存儲(chǔ)。在基于列式存儲(chǔ)的數(shù)據(jù)庫(kù)中, 數(shù)據(jù)是按照列為基礎(chǔ)邏輯存儲(chǔ)單元進(jìn)行存儲(chǔ)的,一列中的數(shù)據(jù)在存儲(chǔ)介質(zhì)中以連續(xù)存儲(chǔ)形式存在。

列存儲(chǔ)的特點(diǎn):因?yàn)槊總€(gè)字段的數(shù)據(jù)聚集存儲(chǔ),在查詢只需要少數(shù)幾個(gè)字段的時(shí)候,能大大減少讀取的數(shù)據(jù)量;每個(gè)字段的數(shù)據(jù)類型一定是相同的,列式存儲(chǔ)可以針對(duì)性的設(shè)計(jì)更好的設(shè)計(jì)壓縮算法。ORC和PARQUET是基于列式存儲(chǔ)的。
舉個(gè)例子吧不然還是太抽象,假設(shè)一個(gè)表有10億行數(shù)據(jù),按照列式存儲(chǔ)的定義,應(yīng)該先將某個(gè)字段的10億條數(shù)據(jù)存儲(chǔ)完之后,再存儲(chǔ)其他字段。
常見的數(shù)據(jù)格式
Hive 支持一下幾種存儲(chǔ)格式,下面我們會(huì)對(duì)每種格式的特點(diǎn)進(jìn)行簡(jiǎn)單介紹
Text File SequenceFile RCFile Avro Files ORC Files Parquet Custom INPUTFORMAT and OUTPUTFORMAT(用戶自定義格式)
Hive 默認(rèn)使用的實(shí)Text File,也就是說當(dāng)你建表的時(shí)候不指定文件的存儲(chǔ)格式的時(shí)候,它就使用的就是Text File,Hive 是支持指定默認(rèn)存儲(chǔ)格式的
<property>
<name>hive.default.fileformat</name>
<value>TextFile</value>
<description>
Expects one of [textfile, sequencefile, rcfile, orc, parquet].
Default file format for CREATE TABLE statement. Users can explicitly override it by CREATE TABLE ... STORED AS [FORMAT]
</description>
</property>
TextFile
存儲(chǔ)方式:行存儲(chǔ)
默認(rèn)的存儲(chǔ)格式,數(shù)據(jù)不做壓縮,磁盤開銷大,數(shù)據(jù)解析開銷大??山Y(jié)合Gzip、Bzip2使用(系統(tǒng)自動(dòng)檢查,執(zhí)行查詢時(shí)自動(dòng)解壓),但使用這種方式,壓縮后的文件不支持split,Hive不會(huì)對(duì)數(shù)據(jù)進(jìn)行切分,從而無法對(duì)數(shù)據(jù)進(jìn)行并行操作。
并且在反序列化過程中,必須逐個(gè)字符判斷是不是分隔符和行結(jié)束符,因此反序列化開銷會(huì)比SequenceFile高幾十倍。
SequenceFile
SequenceFile是Hadoop API提供的一種二進(jìn)制文件支持,,存儲(chǔ)方式為行存儲(chǔ),其具有使用方便、可分割、可壓縮的特點(diǎn)。
壓縮數(shù)據(jù)文件可以節(jié)省磁盤空間,但Hadoop中有些原生壓縮文件的就是不支持分割,所以Hadoop 猜提供了SequenceFile 這種格式,支持分割的文件可以并行的有多個(gè)mapper程序處理大數(shù)據(jù)文件,大多數(shù)文件不支持可分割是因?yàn)檫@些文件只能從頭開始讀。
SequenceFile支持三種壓縮選擇:NONE,RECORD,BLOCK。Record壓縮率低,一般建議使用BLOCK壓縮,RECORD是默認(rèn)選項(xiàng),通常BLOCK會(huì)帶來較RECORD更好的壓縮性能。
SequenceFile的優(yōu)勢(shì)是文件和hadoop api中的MapFile是相互兼容的。
注:建表使用這個(gè)格式,導(dǎo)入數(shù)據(jù)時(shí)會(huì)直接把數(shù)據(jù)文件拷貝到hdfs上不進(jìn)行處理。SequenceFile、RCFile、ORC格式的表不能直接從本地文件導(dǎo)入數(shù)據(jù),數(shù)據(jù)要先導(dǎo)入到TextFile格式的表中,然后再?gòu)腡extFile表中用insert導(dǎo)入到SequenceFile、RCFile表中
RCfile
存儲(chǔ)方式:數(shù)據(jù)按行分塊,每塊按列存儲(chǔ)
Record Columnar的縮寫,是Hadoop中第一個(gè)列式存儲(chǔ)格式。能夠很好的壓縮和快速的查詢性能,但是不支持模式演進(jìn)。是一種行列存儲(chǔ)相結(jié)合的存儲(chǔ)方式。
首先,其將數(shù)據(jù)按行分塊,保同一行的數(shù)據(jù)位于同一個(gè)塊上,避免讀一個(gè)記錄需要讀取多個(gè)block。其次,塊數(shù)據(jù)列式存儲(chǔ),有利于數(shù)據(jù)壓縮和快速的列存取,并且能跳過不必要的列讀取
ORCfile
存儲(chǔ)方式:數(shù)據(jù)按行分塊 每塊按照列存儲(chǔ)(不是真正意義上的列存儲(chǔ),可以理解為分段列存儲(chǔ),你可以對(duì)照我們講的那個(gè)例子來理解)
ORC的全稱是(Optimized Row Columnar),ORC文件格式是一種Hadoop生態(tài)圈中的列式存儲(chǔ)格式,它的產(chǎn)生早在2013年初,最初產(chǎn)生自Apache Hive,用于降低Hadoop數(shù)據(jù)存儲(chǔ)空間和加速Hive查詢速度。和Parquet類似,它并不是一個(gè)單純的列式存儲(chǔ)格式,仍然是首先根據(jù)行組分割整個(gè)表,在每一個(gè)行組內(nèi)進(jìn)行按列存儲(chǔ)。
ORC文件是自描述的,它的元數(shù)據(jù)使用Protocol Buffers序列化,并且文件中的數(shù)據(jù)盡可能的壓縮以降低存儲(chǔ)空間的消耗,目前也被Spark SQL、Presto等查詢引擎支持,但是Impala對(duì)于ORC目前沒有支持,仍然使用Parquet作為主要的列式存儲(chǔ)格式。2015年ORC項(xiàng)目被Apache項(xiàng)目基金會(huì)提升為Apache頂級(jí)項(xiàng)目。
ORC文件特點(diǎn)是壓縮快 快速列存取,是rcfile的改良版本,相比RC能夠更好的壓縮,能夠更快的查詢,支持各種復(fù)雜的數(shù)據(jù)類型,比如datetime,decimal,以及復(fù)雜的struct是以二進(jìn)制方式存儲(chǔ)的,所以是不可以直接讀取,ORC文件也是自解析的,它包含許多的元數(shù)據(jù),這些元數(shù)據(jù)都是同構(gòu)ProtoBuffer進(jìn)行序列化的。
需要注意的是 ORC在讀寫時(shí)候需要消耗額外的CPU資源來壓縮和解壓縮,當(dāng)然這部分的CPU消耗是非常少的。
格式
ORC文件:保存在文件系統(tǒng)上的普通二進(jìn)制文件,一個(gè)ORC文件中可以包含多個(gè)stripe,每個(gè)Orc文件由1個(gè)或多個(gè)stripe組成,每個(gè)stripe一般為HDFS的塊大小,每一個(gè)stripe包含多條記錄,這些記錄按照列進(jìn)行獨(dú)立存儲(chǔ),對(duì)應(yīng)到Parquet中就是row group的概念。每個(gè)Stripe里有三部分組成,分別是Index Data,Row Data,Stripe Footer;
stripe:一組行形成一個(gè)stripe,每次讀取文件是以行組為單位的,一般為HDFS的塊大小,保存了每一列的索引和數(shù)據(jù)。
文件級(jí)元數(shù)據(jù):包括文件的描述信息PostScript、文件meta信息(包括整個(gè)文件的統(tǒng)計(jì)信息)、所有stripe的信息和文件schema信息。
stripe元數(shù)據(jù):保存stripe的位置、每一個(gè)列的在該stripe的統(tǒng)計(jì)信息以及所有的stream類型和位置。
row group:索引的最小單位,一個(gè)stripe中包含多個(gè)row group,默認(rèn)為10000個(gè)值組成。每次讀取文件是以行組為單位的,一般為HDFS的塊大小,保存了每一列的索引和數(shù)據(jù)。

在ORC文件中保存了三個(gè)層級(jí)的統(tǒng)計(jì)信息,分別為文件級(jí)別、stripe級(jí)別和row group級(jí)別的,他們都可以用來根據(jù)Search ARGuments(謂詞下推條件)判斷是否可以跳過某些數(shù)據(jù),在統(tǒng)計(jì)信息中都包含成員數(shù)和是否有null值,并且對(duì)于不同類型的數(shù)據(jù)設(shè)置一些特定的統(tǒng)計(jì)信息。
file level:在ORC文件的末尾會(huì)記錄文件級(jí)別的統(tǒng)計(jì)信息,會(huì)記錄整個(gè)文件中columns的統(tǒng)計(jì)信息。這些信息主要用于查詢的優(yōu)化,也可以為一些簡(jiǎn)單的聚合查詢比如max, min, sum輸出結(jié)果。
**stripe level:**ORC文件會(huì)保存每個(gè)字段stripe級(jí)別的統(tǒng)計(jì)信息,ORC reader使用這些統(tǒng)計(jì)信息來確定對(duì)于一個(gè)查詢語(yǔ)句來說,需要讀入哪些stripe中的記錄。比如說某個(gè)stripe的字段max(a)=10,min(a)=3,那么當(dāng)where條件為a >10或者a <3時(shí),那么這個(gè)stripe中的所有記錄在查詢語(yǔ)句執(zhí)行時(shí)不會(huì)被讀入
row level: 為了進(jìn)一步的避免讀入不必要的數(shù)據(jù),在邏輯上將一個(gè)column的index以一個(gè)給定的值(默認(rèn)為10000,可由參數(shù)配置)分割為多個(gè)index組。以10000條記錄為一個(gè)組,對(duì)數(shù)據(jù)進(jìn)行統(tǒng)計(jì)。Hive查詢引擎會(huì)將where條件中的約束傳遞給ORC reader,這些reader根據(jù)組級(jí)別的統(tǒng)計(jì)信息,過濾掉不必要的數(shù)據(jù)。如果該值設(shè)置的太小,就會(huì)保存更多的統(tǒng)計(jì)信息,用戶需要根據(jù)自己數(shù)據(jù)的特點(diǎn)權(quán)衡一個(gè)合理的值
數(shù)據(jù)訪問
讀取ORC文件是從尾部開始的,第一次讀取16KB的大小,盡可能的將Postscript和Footer數(shù)據(jù)都讀入內(nèi)存。文件的最后一個(gè)字節(jié)保存著PostScript的長(zhǎng)度,它的長(zhǎng)度不會(huì)超過256字節(jié),PostScript中保存著整個(gè)文件的元數(shù)據(jù)信息,它包括文件的壓縮格式、文件內(nèi)部每一個(gè)壓縮塊的最大長(zhǎng)度(每次分配內(nèi)存的大小)、Footer長(zhǎng)度,以及一些版本信息。在Postscript和Footer之間存儲(chǔ)著整個(gè)文件的統(tǒng)計(jì)信息(上圖中未畫出),這部分的統(tǒng)計(jì)信息包括每一個(gè)stripe中每一列的信息,主要統(tǒng)計(jì)成員數(shù)、最大值、最小值、是否有空值等。
接下來讀取文件的Footer信息,它包含了每一個(gè)stripe的長(zhǎng)度和偏移量,該文件的schema信息(將schema樹按照schema中的編號(hào)保存在數(shù)組中)、整個(gè)文件的統(tǒng)計(jì)信息以及每一個(gè)row group的行數(shù)。
處理stripe時(shí)首先從Footer中獲取每一個(gè)stripe的其實(shí)位置和長(zhǎng)度、每一個(gè)stripe的Footer數(shù)據(jù)(元數(shù)據(jù),記錄了index和data的的長(zhǎng)度),整個(gè)striper被分為index和data兩部分,stripe內(nèi)部是按照row group進(jìn)行分塊的(每一個(gè)row group中多少條記錄在文件的Footer中存儲(chǔ)),row group內(nèi)部按列存儲(chǔ)。每一個(gè)row group由多個(gè)stream保存數(shù)據(jù)和索引信息。每一個(gè)stream的數(shù)據(jù)會(huì)根據(jù)該列的類型使用特定的壓縮算法保存。在ORC中存在如下幾種stream類型:
PRESENT:每一個(gè)成員值在這個(gè)stream中保持一位(bit)用于標(biāo)示該值是否為NULL,通過它可以只記錄部位NULL的值 DATA:該列的中屬于當(dāng)前stripe的成員值。 LENGTH:每一個(gè)成員的長(zhǎng)度,這個(gè)是針對(duì)string類型的列才有的。 DICTIONARY_DATA:對(duì)string類型數(shù)據(jù)編碼之后字典的內(nèi)容。 SECONDARY:存儲(chǔ)Decimal、timestamp類型的小數(shù)或者納秒數(shù)等。 ROW_INDEX:保存stripe中每一個(gè)row group的統(tǒng)計(jì)信息和每一個(gè)row group起始位置信息。
在初始化階段獲取全部的元數(shù)據(jù)之后,可以通過includes數(shù)組指定需要讀取的列編號(hào),它是一個(gè)boolean數(shù)組,如果不指定則讀取全部的列,還可以通過傳遞SearchArgument參數(shù)指定過濾條件,根據(jù)元數(shù)據(jù)首先讀取每一個(gè)stripe中的index信息,然后根據(jù)index中統(tǒng)計(jì)信息以及SearchArgument參數(shù)確定需要讀取的row group編號(hào),再根據(jù)includes數(shù)據(jù)決定需要從這些row group中讀取的列,通過這兩層的過濾需要讀取的數(shù)據(jù)只是整個(gè)stripe多個(gè)小段的區(qū)間,然后ORC會(huì)盡可能合并多個(gè)離散的區(qū)間盡可能的減少I/O次數(shù)。然后再根據(jù)index中保存的下一個(gè)row group的位置信息調(diào)至該stripe中第一個(gè)需要讀取的row group中。
使用ORC文件格式時(shí),用戶可以使用HDFS的每一個(gè)block存儲(chǔ)ORC文件的一個(gè)stripe。對(duì)于一個(gè)ORC文件來說,stripe的大小一般需要設(shè)置得比HDFS的block小,如果不這樣的話,一個(gè)stripe就會(huì)分別在HDFS的多個(gè)block上,當(dāng)讀取這種數(shù)據(jù)時(shí)就會(huì)發(fā)生遠(yuǎn)程讀數(shù)據(jù)的行為。如果設(shè)置stripe的只保存在一個(gè)block上的話,如果當(dāng)前block上的剩余空間不足以存儲(chǔ)下一個(gè)strpie,ORC的writer接下來會(huì)將數(shù)據(jù)打散保存在block剩余的空間上,直到這個(gè)block存滿為止。這樣,下一個(gè)stripe又會(huì)從下一個(gè)block開始存儲(chǔ)。
由于ORC中使用了更加精確的索引信息,使得在讀取數(shù)據(jù)時(shí)可以指定從任意一行開始讀取,更細(xì)粒度的統(tǒng)計(jì)信息使得讀取ORC文件跳過整個(gè)row group,ORC默認(rèn)會(huì)對(duì)任何一塊數(shù)據(jù)和索引信息使用ZLIB壓縮,因此ORC文件占用的存儲(chǔ)空間也更小
Parquet
Parquet能夠很好的壓縮,有很好的查詢性能,支持有限的模式演進(jìn)。但是寫速度通常比較慢。這中文件格式主要是用在Cloudera Impala上面的。Parquet文件是以二進(jìn)制方式存儲(chǔ)的,所以是不可以直接讀取的,文件中包括該文件的數(shù)據(jù)和元數(shù)據(jù),因此Parquet格式文件是自解析的。
Parquet的設(shè)計(jì)方案,整體來看,基本照搬了Dremel中對(duì)嵌套數(shù)據(jù)結(jié)構(gòu)的打平和重構(gòu)算法,通過高效的數(shù)據(jù)打平和重建算法,實(shí)現(xiàn)按列存儲(chǔ)(列組),進(jìn)而對(duì)列數(shù)據(jù)引入更具針對(duì)性的編碼和壓縮方案,來降低存儲(chǔ)代價(jià),提升計(jì)算性能。想要了解這一算法邏輯的,可以看Dremel的論文:Dremel: Interactive Analysis of WebScaleDatasets
測(cè)試
準(zhǔn)備測(cè)試數(shù)據(jù)
首先我們生成一份測(cè)試數(shù)據(jù),這是生成數(shù)據(jù)的測(cè)試代碼
public class ProduceTestData {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:MM:ss");
@Test
public void testRandomName() throws IOException {
Faker faker = new Faker(Locale.CHINA);
final Name name = faker.name();
final Address address = faker.address();
Number number = faker.number();
PhoneNumber phoneNumber = faker.phoneNumber();
BufferedWriter out = new BufferedWriter(new FileWriter("/Users/liuwenqiang/access.log"));
int num=0;
while (num<10000000){
int id = number.randomDigitNotZero();
String userName = name.name();
String time = simpleDateFormat.format(new Date(System.currentTimeMillis()));
String city = address.city();
String phonenum = phoneNumber.cellPhone();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(id);
stringBuilder.append("\t");
stringBuilder.append(userName);
stringBuilder.append("\t");
stringBuilder.append(city);
stringBuilder.append("\t");
stringBuilder.append(phonenum);
stringBuilder.append("\t");
stringBuilder.append(time);
out.write(stringBuilder.toString());
out.newLine();
}
out.flush();
out.close();
}
}
下面準(zhǔn)備三張表,分別是log_text、log_orc和log_parquet
create table log_text(
id int,
name string,
city string,
phone string,
acctime string)
row format delimited fields terminated by '\t'
stored as textfile;
LOAD DATA LOCAL INPATH '/Users/liuwenqiang/access.log' OVERWRITE INTO TABLE ods.log_text;
create table log_orc(
id int,
name string,
city string,
phone string,
acctime string)
row format delimited fields terminated by '\t'
stored as orc;
insert overwrite table ods.log_orc select * from ods.log_text;
create table log_parquet(
id int,
name string,
city string,
phone string,
acctime string)
row format delimited fields terminated by '\t'
stored as parquet;
insert overwrite table ods.log_parquet select * from ods.log_text;
所有關(guān)于ORCFile的參數(shù)都是在Hive SQL語(yǔ)句的TBLPROPERTIES字段里面出現(xiàn)
| Key | Default | Notes |
|---|---|---|
| orc.compress | ZLIB | high level compression (one of NONE, ZLIB, SNAPPY) |
| orc.compress.size | 262,144 | number of bytes in each compression chunk |
| orc.compress.size | 262,144 | number of bytes in each compression chunk |
| orc.row.index.stride | 10,000 | number of rows between index entries (must be >= 1000) |
| orc.create.index | true | whether to create row indexes |
存儲(chǔ)空間大小
text

orc

parquet

測(cè)試SQL 執(zhí)行效率
測(cè)試SQL select city,count(1) as cnt from log_text group by city order by cnt desc;
text

orc

parquet

總結(jié)
介紹了行式存儲(chǔ)和列式存儲(chǔ)的特點(diǎn),以及適用場(chǎng)景 介紹了Hive 常見的存儲(chǔ)格式,Parquet 和 ORC都是二進(jìn)制存儲(chǔ)的,都是不可直接讀取的,Parquet和ORC 都是Apache 頂級(jí)項(xiàng)目,Parquet不支持ACID 不支持更新,ORC支持有限的ACID 和 更新 我們簡(jiǎn)單對(duì)比了一下Text、ORCfile 和Parquet的存儲(chǔ)占用和查詢性能,因?yàn)槲覀兊牟樵儽容^簡(jiǎn)單加上數(shù)據(jù)本身不是很大,所以查詢性能差異不是很大,但是占用空間存儲(chǔ)的差異還是很大的
Hive 壓縮
對(duì)于數(shù)據(jù)密集型任務(wù),I/O操作和網(wǎng)絡(luò)數(shù)據(jù)傳輸需要花費(fèi)相當(dāng)長(zhǎng)的時(shí)間才能完成。通過在 Hive 中啟用壓縮功能,我們可以提高 Hive 查詢的性能,并節(jié)省 HDFS 集群上的存儲(chǔ)空間。
HiveQL語(yǔ)句最終都將轉(zhuǎn)換成為hadoop中的MapReduce job,而MapReduce job可以有對(duì)處理的數(shù)據(jù)進(jìn)行壓縮。
首先說明mapreduce哪些過程可以設(shè)置壓縮:需要分析處理的數(shù)據(jù)在進(jìn)入map前可以壓縮,然后解壓處理,map處理完成后的輸出可以壓縮,這樣可以減少網(wǎng)絡(luò)I/O(reduce通常和map不在同一節(jié)點(diǎn)上),reduce拷貝壓縮的數(shù)據(jù)后進(jìn)行解壓,處理完成后可以壓縮存儲(chǔ)在hdfs上,以減少磁盤占用量。

Hive中間數(shù)據(jù)壓縮
提交后,一個(gè)復(fù)雜的 Hive 查詢通常會(huì)轉(zhuǎn)換為一系列多階段 MapReduce 作業(yè),這些作業(yè)將通過 Hive 引擎進(jìn)行鏈接以完成整個(gè)查詢。因此,這里的 ‘中間輸出’ 是指前一個(gè) MapReduce 作業(yè)的輸出,將會(huì)作為下一個(gè) MapReduce 作業(yè)的輸入數(shù)據(jù)。
可以通過使用 Hive Shell 中的 set 命令或者修改 hive-site.xml 配置文件來修改 hive.exec.compress.intermediate 屬性,這樣我們就可以在 Hive Intermediate 輸出上啟用壓縮。
hive.exec.compress.intermediate:默認(rèn)為false,設(shè)置true為激活中間數(shù)據(jù)壓縮功能,就是MapReduce的shuffle階段對(duì)mapper產(chǎn)生中間壓縮??梢允褂?set 命令在 hive shell 中設(shè)置這些屬性
set hive.exec.compress.intermediate=true
set mapred.map.output.compression.codec= org.apache.hadoop.io.compress.SnappyCodec
或者
set hive.exec.compress.intermediate=true
set mapred.map.output.compression.codec=com.hadoop.compression.lzo.LzoCodec
也可以在配置文件中進(jìn)行配置
<property>
<name>hive.exec.compress.intermediate</name>
<value>true</value>
<description>
This controls whether intermediate files produced by Hive between multiple map-reduce jobs are compressed.
The compression codec and other options are determined from Hadoop config variables mapred.output.compress*
</description>
</property>
<property>
<name>hive.intermediate.compression.codec</name>
<value>org.apache.hadoop.io.compress.SnappyCodec</value>
<description/>
</property>
最終輸出結(jié)果壓縮
hive.exec.compress.output:用戶可以對(duì)最終生成的Hive表的數(shù)據(jù)通常也需要壓縮。該參數(shù)控制這一功能的激活與禁用,設(shè)置為true來聲明將結(jié)果文件進(jìn)行壓縮。
mapred.output.compression.codec:將hive.exec.compress.output參數(shù)設(shè)置成true后,然后選擇一個(gè)合適的編解碼器,如選擇SnappyCodec。設(shè)置如下(兩種壓縮的編寫方式是一樣的):
set hive.exec.compress.output=true
set mapred.output.compression.codec=org.apache.hadoop.io.compress.SnappyCodec
或者
set mapred.output.compress=true
set mapred.output.compression.codec=org.apache.hadoop.io.compress.LzopCodec
同樣可以通過配置文件配置
<property>
<name>hive.exec.compress.output</name>
<value>true</value>
<description>
This controls whether the final outputs of a query (to a local/HDFS file or a Hive table) is compressed.
The compression codec and other options are determined from Hadoop config variables mapred.output.compress*
</description>
</property>
常見的壓縮格式
Hive支持的壓縮格式有bzip2、gzip、deflate、snappy、lzo等。Hive依賴Hadoop的壓縮方法,所以Hadoop版本越高支持的壓縮方法越多,可以在$HADOOP_HOME/conf/core-site.xml中進(jìn)行配置:
<property>
<name>io.compression.codecs</name>
<value>org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.DefaultCodec,com.hadoop.compression.lzo.LzoCodec,com.hadoop.compression.lzo.LzopCodec,org.apache.hadoop.io.compress.BZip2Codec
</value>
</property>
<property>
<property>
<name>io.compression.codec.lzo.class</name>
<value>com.hadoop.compression.lzo.LzoCodec</value>
</property>
需要注意的是在我們?cè)趆ive配置開啟壓縮之前,我們需要配置讓Hadoop 支持,因?yàn)閔ive 開啟壓縮只是指明了使用哪一種壓縮算法,具體的配置還是需要在Hadoop 中配置
常見的壓縮格式有:

其中壓縮比bzip2 > zlib > gzip > deflate > snappy > lzo > lz4,在不同的測(cè)試場(chǎng)景中,會(huì)有差異,這僅僅是一個(gè)大概的排名情況。bzip2、zlib、gzip、deflate可以保證最小的壓縮,但在運(yùn)算中過于消耗時(shí)間。
從壓縮性能上來看:lz4 > lzo > snappy > deflate > gzip > bzip2,其中l(wèi)z4、lzo、snappy壓縮和解壓縮速度快,壓縮比低。
所以一般在生產(chǎn)環(huán)境中,經(jīng)常會(huì)采用lz4、lzo、snappy壓縮,以保證運(yùn)算效率。
| 壓縮格式 | 對(duì)應(yīng)的編碼/解碼 |
|---|---|
| DEFAULT | org.apache.hadoop.io.compress.DefaultCodec |
| Gzip | org.apache.hadoop.io.compress.GzipCodec |
| Bzip | org.apache.hadoop.io.compress.BzipCodec |
| Snappy | org.apache.hadoop.io.compress.SnappyCodec |
| Lzo | org.apache.hadoop.io.compress.LzopCodec |
對(duì)于使用 Gzip or Bzip2 壓縮的文件我們是可以直接導(dǎo)入到text 存儲(chǔ)類型的表中的,hive 會(huì)自動(dòng)幫我們完成數(shù)據(jù)的解壓
CREATE TABLE raw (line STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n';
LOAD DATA LOCAL INPATH '/tmp/weblogs/20090603-access.log.gz' INTO TABLE raw;
Native Libraries
Hadoop由Java語(yǔ)言開發(fā),所以壓縮算法大多由Java實(shí)現(xiàn);但有些壓縮算法并不適合Java進(jìn)行實(shí)現(xiàn),會(huì)提供本地庫(kù)Native Libraries補(bǔ)充支持。Native Libraries除了自帶bzip2, lz4, snappy, zlib壓縮方法外,還可以自定義安裝需要的功能庫(kù)(snappy、lzo等)進(jìn)行擴(kuò)展。而且使用本地庫(kù)Native Libraries提供的壓縮方式,性能上會(huì)有50%左右的提升。
使用命令可以查看native libraries的加載情況:
hadoop checknative -a

完成對(duì)Hive表的壓縮,有兩種方式:配置MapReduce壓縮、開啟Hive表壓縮功能。因?yàn)镠ive會(huì)將SQL作業(yè)轉(zhuǎn)換為MapReduce任務(wù),所以直接對(duì)MapReduce進(jìn)行壓縮配置,可以達(dá)到壓縮目的;當(dāng)然為了方便起見,Hive中的特定表支持壓縮屬性,自動(dòng)完成壓縮的功能。
Hive中的可用壓縮編解碼器
要在 Hive 中啟用壓縮,首先我們需要找出 Hadoop 集群上可用的壓縮編解碼器,我們可以使用下面的 set 命令列出可用的壓縮編解碼器。
hive> set io.compression.codecs;
io.compression.codecs=
org.apache.hadoop.io.compress.GzipCodec,
org.apache.hadoop.io.compress.DefaultCodec,
org.apache.hadoop.io.compress.BZip2Codec,
org.apache.hadoop.io.compress.SnappyCodec,
com.hadoop.compression.lzo.LzoCodec,
com.hadoop.compression.lzo.LzopCodec
演示
首先我們創(chuàng)建一個(gè)未經(jīng)壓縮的表tmp_no_compress
CREATE TABLE tmp_no_compress ROW FORMAT DELIMITED LINES TERMINATED BY '\n'
AS SELECT * FROM log_text;
我們看一下不設(shè)置壓縮屬性的輸出

在 Hive Shell 中設(shè)置壓縮屬性:
set hive.exec.compress.output=true;
set mapreduce.output.fileoutputformat.compress=true;
set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec;
set mapreduce.output.fileoutputformat.compress.type=BLOCK;
根據(jù)現(xiàn)有表 tmp_order_id 創(chuàng)建一個(gè)壓縮后的表 tmp_order_id_compress:
CREATE TABLE tmp_compress ROW FORMAT DELIMITED LINES TERMINATED BY '\n'
AS SELECT * FROM log_text;
我們?cè)诳匆幌略O(shè)置壓縮屬性后輸出:

總結(jié)
數(shù)據(jù)壓縮可以發(fā)生在哪些階段 1 輸入數(shù)據(jù)可以壓縮后的數(shù)據(jù) 2 中間的數(shù)據(jù)可以壓縮 3 輸出的數(shù)據(jù)可以壓縮 hive 僅僅是配置了開啟壓縮和使用哪種壓縮方式,真正的配置是在hadoop 中配置的,而數(shù)據(jù)的壓縮是在MapReduce 中發(fā)生的 對(duì)于數(shù)據(jù)密集型任務(wù),I/O操作和網(wǎng)絡(luò)數(shù)據(jù)傳輸需要花費(fèi)相當(dāng)長(zhǎng)的時(shí)間才能完成。通過在 Hive 中啟用壓縮功能,我們可以提高 Hive 查詢的性能,并節(jié)省 HDFS 集群上的存儲(chǔ)空間。
