數(shù)棧技術(shù)分享:Hive優(yōu)化之配置參數(shù)的優(yōu)化
Hive是大數(shù)據(jù)領(lǐng)域常用的組件之一,主要用于大數(shù)據(jù)離線(xiàn)數(shù)倉(cāng)的運(yùn)算,關(guān)于Hive的性能調(diào)優(yōu)在日常工作和面試中是經(jīng)常涉及的一個(gè)點(diǎn),因此掌握一些Hive調(diào)優(yōu)是必不可少的一項(xiàng)技能。影響Hive效率的主要因素有數(shù)據(jù)傾斜、數(shù)據(jù)冗余、job的IO以及不同底層引擎配置情況和Hive本身參數(shù)和HiveSQL的執(zhí)行等。本文主要從建表配置參數(shù)方面對(duì)Hive優(yōu)化進(jìn)行講解。
1. 創(chuàng)建一個(gè)普通表
table test_user1(id int, name string,code string,code_id string ) ROW FORMAT DELIMITED FIELDS TERMINATED ?BY ',';
2. 查看這張表的信息
DESCRIBE FORMATTED ?test_user1;

我們從該表的描述信息介紹建表時(shí)的一些可優(yōu)化點(diǎn)。
2.1 表的文件數(shù)
numFiles表示表中含有的文件數(shù),當(dāng)文件數(shù)過(guò)多時(shí)可能意味著該表的小文件過(guò)多,這時(shí)候我們可以針對(duì)小文件的問(wèn)題進(jìn)行一些優(yōu)化,HDFS本身提供了解決方案:
(1)Hadoop Archive/HAR:將小文件打包成大文件。
(2)SEQUENCEFILE格式:將大量小文件壓縮成一個(gè)SEQUENCEFILE文件。
(3)CombineFileInputFormat:在map和reduce處理之前組合小文件。
(4)HDFS Federation:HDFS聯(lián)盟,使用多個(gè)namenode節(jié)點(diǎn)管理文件。
除此之外,我們還可以通過(guò)設(shè)置hive的參數(shù)來(lái)合并小文件。
(1)輸入階段合并
需要更改Hive的輸入文件格式,即參數(shù)hive.input.format,默認(rèn)值是org.apache.hadoop.hive.ql.io.HiveInputFormat,我們改成org.apache.hadoop.hive.ql.io.CombineHiveInputFormat。這樣比起上面對(duì)mapper數(shù)的調(diào)整,會(huì)多出兩個(gè)參數(shù),分別是mapred.min.split.size.per.node和mapred.min.split.size.per.rack,含義是單節(jié)點(diǎn)和單機(jī)架上的最小split大小。如果發(fā)現(xiàn)有split大小小于這兩個(gè)值(默認(rèn)都是100MB),則會(huì)進(jìn)行合并。具體邏輯可以參看Hive源碼中的對(duì)應(yīng)類(lèi)。
(2)輸出階段合并
直接將hive.merge.mapfiles和hive.merge.mapredfiles都設(shè)為true即可,前者表示將map-only任務(wù)的輸出合并,后者表示將map-reduce任務(wù)的輸出合并,Hive會(huì)額外啟動(dòng)一個(gè)mr作業(yè)將輸出的小文件合并成大文件。另外,hive.merge.size.per.task可以指定每個(gè)task輸出后合并文件大小的期望值,hive.merge.size.smallfiles.avgsize可以指定所有輸出文件大小的均值閾值,默認(rèn)值都是1GB。如果平均大小不足的話(huà),就會(huì)另外啟動(dòng)一個(gè)任務(wù)來(lái)進(jìn)行合并。
2.2 表的存儲(chǔ)格式
通過(guò)InputFormat和OutputFormat可以看出表的存儲(chǔ)格式是TEXT類(lèi)型,Hive支持TEXTFILE, SEQUENCEFILE, AVRO, RCFILE, ORC,以及PARQUET文件格式,可以通過(guò)兩種方式指定表的文件格式:
(1)CREATE TABLE ... STORE AS <file_format>:在建表時(shí)指定文件格式,默認(rèn)是TEXTFILE
(2)ALTER TABLE ... [PARTITION partition_spec] SET FILEFORMAT <file_format>:修改具體表的文件格式
如果要改變創(chuàng)建表的默認(rèn)文件格式,可以使用set hive.default.fileformat=<file_format>進(jìn)行配置,適用于所有表。同時(shí)也可以使用set hive.default.fileformat.managed = <file_format>進(jìn)行配置,僅適用于內(nèi)部表或外部表。
擴(kuò)展:不同存儲(chǔ)方式的情況
TEXT, SEQUENCE和 AVRO文件是面向行的文件存儲(chǔ)格式,不是最佳的文件格式,因?yàn)榧幢阒徊樵?xún)一列數(shù)據(jù),使用這些存儲(chǔ)格式的表也需要讀取完整的一行數(shù)據(jù)。另一方面,面向列的存儲(chǔ)格式(RCFILE, ORC, PARQUET)可以很好地解決上面的問(wèn)題。關(guān)于每種文件格式的說(shuō)明,如下:
(1)TEXTFILE
創(chuàng)建表時(shí)的默認(rèn)文件格式,數(shù)據(jù)被存儲(chǔ)成文本格式。文本文件可以被分割和并行處理,也可以使用壓縮,比如GZip、LZO或者Snappy。然而大部分的壓縮文件不支持分割和并行處理,會(huì)造成一個(gè)作業(yè)只有一個(gè)mapper去處理數(shù)據(jù),使用壓縮的文本文件要確保文件不要過(guò)大,一般接近兩個(gè)HDFS塊的大小。
(2)SEQUENCEFILE
key/value對(duì)的二進(jìn)制存儲(chǔ)格式,sequence文件的優(yōu)勢(shì)是比文本格式更好壓縮,sequence文件可以被壓縮成塊級(jí)別的記錄,塊級(jí)別的壓縮是一個(gè)很好的壓縮比例。如果使用塊壓縮,需要使用下面的配置:set hive.exec.compress.output=true; set io.seqfile.compression.type=BLOCK
(3)AVRO
二進(jìn)制格式文件,除此之外,avro也是一個(gè)序列化和反序列化的框架。avro提供了具體的數(shù)據(jù)schema。
(4)RCFILE
全稱(chēng)是Record Columnar File,首先將表分為幾個(gè)行組,對(duì)每個(gè)行組內(nèi)的數(shù)據(jù)進(jìn)行按列存儲(chǔ),每一列的數(shù)據(jù)都是分開(kāi)存儲(chǔ),即先水平劃分,再垂直劃分。
(5)ORC
全稱(chēng)是Optimized Row Columnar,從hive0.11版本開(kāi)始支持,ORC格式是RCFILE格式的一種優(yōu)化的格式,提供了更大的默認(rèn)塊(256M)
(6)PARQUET
另外一種列式存儲(chǔ)的文件格式,與ORC非常類(lèi)似,與ORC相比,Parquet格式支持的生態(tài)更廣,比如低版本的impala不支持ORC格式。
配置同樣數(shù)據(jù)同樣字段的兩張表,以常見(jiàn)的TEXT行存儲(chǔ)和ORC列存儲(chǔ)兩種存儲(chǔ)方式為例,對(duì)比執(zhí)行速度。
TEXT存儲(chǔ)方式


總結(jié):從上圖中可以看出列存儲(chǔ)在對(duì)指定列進(jìn)行查詢(xún)時(shí),速度更快,建議在建表時(shí)設(shè)置列存儲(chǔ)的存儲(chǔ)方式。
2.3 表的壓縮
對(duì)Hive表進(jìn)行壓縮是常見(jiàn)的優(yōu)化手段,一些存儲(chǔ)方式自帶壓縮選擇,比如SEQUENCEFILE支持三種壓縮選擇:NONE,RECORD,BLOCK。Record壓縮率低,一般建議使用BLOCK壓縮;
ORC支持三種壓縮選擇:NONE,ZLIB,SNAPPY。我們以TEXT存儲(chǔ)方式和ORC存儲(chǔ)方式為例,查看表的壓縮情況。
配置同樣數(shù)據(jù)同樣字段的四張表,一張TEXT存儲(chǔ)方式,另外三張分別是默認(rèn)壓縮方式的ORC存儲(chǔ)、SNAPPY壓縮方式的ORC存儲(chǔ)和NONE壓縮方式的ORC存儲(chǔ),查看在hdfs上的存儲(chǔ)情況:
TEXT存儲(chǔ)方式

默認(rèn)壓縮ORC存儲(chǔ)方式

SNAPPY壓縮的ORC存儲(chǔ)方式

NONE壓縮的ORC存儲(chǔ)方式

總結(jié):可以看到ORC存儲(chǔ)方式將數(shù)據(jù)存放為兩個(gè)block,默認(rèn)壓縮大小加起來(lái)134.69M,SNAPPY壓縮大小加起來(lái)196.67M,NONE壓縮大小加起來(lái)247.55M,TEXT存儲(chǔ)方式的文件大小為366.58M,且默認(rèn)block兩種存儲(chǔ)方式分別為256M和128M,ORC默認(rèn)的壓縮方式比SNAPPY壓縮得到的文件還小,原因是ORZ默認(rèn)的ZLIB壓縮方式采用的是deflate壓縮算法,比Snappy壓縮算法得到的壓縮比高,壓縮的文件更小。ORC不同壓縮方式之間的執(zhí)行速度,經(jīng)過(guò)多次測(cè)試發(fā)現(xiàn)三種壓縮方式的執(zhí)行速度差不多,所以建議采用ORC默認(rèn)的存儲(chǔ)方式進(jìn)行存儲(chǔ)數(shù)據(jù)。
2.4 分桶分區(qū)
Num Buckets表示桶的數(shù)量,我們可以通過(guò)分桶和分區(qū)操作對(duì)Hive表進(jìn)行優(yōu)化:
對(duì)于一張較大的表,可以將它設(shè)計(jì)成分區(qū)表,如果不設(shè)置成分區(qū)表,數(shù)據(jù)是全盤(pán)掃描的,設(shè)置成分區(qū)表后,查詢(xún)時(shí)只在指定的分區(qū)中進(jìn)行數(shù)據(jù)掃描,提升查詢(xún)效率。要注意盡量避免多級(jí)分區(qū),一般二級(jí)分區(qū)足夠使用。常見(jiàn)的分區(qū)字段:
(1)日期或者時(shí)間,比如year、month、day或者h(yuǎn)our,當(dāng)表中存在時(shí)間或者日期字段時(shí),可以使用些字段。
(2)地理位置,比如國(guó)家、省份、城市等
(3)業(yè)務(wù)邏輯,比如部門(mén)、銷(xiāo)售區(qū)域、客戶(hù)等等
與分區(qū)表類(lèi)似,分桶表的組織方式是將HDFS上的一張大表文件分割成多個(gè)文件。分桶是相對(duì)分區(qū)進(jìn)行更細(xì)粒度的劃分,分桶將整個(gè)數(shù)據(jù)內(nèi)容按照分桶字段屬性值得hash值進(jìn)行區(qū)分,分桶可以加快數(shù)據(jù)采樣,也可以提升join的性能(join的字段是分桶字段),因?yàn)榉滞翱梢源_保某個(gè)key對(duì)應(yīng)的數(shù)據(jù)在一個(gè)特定的桶內(nèi)(文件),所以巧妙地選擇分桶字段可以大幅度提升join的性能。通常情況下,分桶字段可以選擇經(jīng)常用在過(guò)濾操作或者join操作的字段。
創(chuàng)建分桶表
create table test_user_bucket(id int, name string,code string,code_id string ) clustered by(id) into 3 buckets ROW FORMAT DELIMITED FIELDS TERMINATED ?BY ',';
查看描述信息
DESCRIBE FORMATTED test_user_bucket
多出了如下信息

查看該表的hdfs

同樣的數(shù)據(jù)查看普通表和分桶表查詢(xún)效率
普通表

分桶表

普通表是全表掃描,分桶表在按照分桶字段的hash值分桶后,根據(jù)join字段或者where過(guò)濾字段在特定的桶中進(jìn)行掃描,效率提升。
數(shù)棧是云原生—站式數(shù)據(jù)中臺(tái)PaaS,我們?cè)趃ithub和gitee上有一個(gè)有趣的開(kāi)源項(xiàng)目:FlinkX,F(xiàn)linkX是一個(gè)基于Flink的批流統(tǒng)一的數(shù)據(jù)同步工具,既可以采集靜態(tài)的數(shù)據(jù),也可以采集實(shí)時(shí)變化的數(shù)據(jù),是全域、異構(gòu)、批流一體的數(shù)據(jù)同步引擎。大家喜歡的話(huà)請(qǐng)給我們點(diǎn)個(gè)star!star!star!
github開(kāi)源項(xiàng)目:https://github.com/DTStack/flinkx
gitee開(kāi)源項(xiàng)目:https://gitee.com/dtstack_dev_0/flinkx