Hive的優化歷程
公司的系統想要轉型,由我和專案經理兩個人來完成從傳統的資料庫向HIVE+HADOOP_+SPARK,用以滿足日益膨脹的大量資料。
對於將資料儲存在Hive,進行了以下的優化:
1,Hive的引擎目前為止有三種,分別為MR,TEZ,SPRAK.由於公司用的是Hive1.2.1,spark是 老版本1.6.2,我查了hive on spark 的網頁後發現這個hive version 不支援我目前這個版本的 spark,因此轉向研究這兩種引擎的區別.
beeline->set hive.execution.engine=tez;
beeline->set hive.execution.engine=mr;
beeline->set hive.execution.engine=spark;
研究前,我對tez並不瞭解,因此檢視官網的文件是最好的入門,這裡應該檢視Hive on TEZ
,來初步瞭解Hive on TEZ的優勢和特點.
個人的總結如下:
(1)當引擎為MR的時候,如果有稍微複雜的操作,比如內外連線,MR會轉化為多個MR,當上一組MR執行完後下一組MR會繼續執行,每一次Maper都會從HDFS讀取檔案,而Reducer會從HDFS寫入檔案,並且Reducer也會從一到多個Maper讀取檔案,造成頻繁的IO開銷。如果採用引擎為TEZ的話,結構可以轉化成MRR,即通過一個或多個Mapper之後,進入一個或多個Reducer,當Reducer結束之後,不寫入hdfs,而是直接下得到的資料轉入給下一組Reducer,重複知道最後得到結果並將其寫入hdfs。避免了過多的IO流
(2)TEZ可以將小的資料集放入記憶體中進行運算,這一點MR做不到
2,要優化查詢語句,這一點無論什麼資料平臺都是通用的
(1) 關鍵詞join的優化
當使用 表a join 表 b的時候,hive會預設將左邊的表快取起來,對右邊的表進行掃描,基於這樣的機制,在使用
join 關鍵字的時候,將小表放在左邊對於提高效能有一定的好處;
注意的是:
對於多個表的join,如果所有的表中只有一張是小表,可以採取以下的優化:
Map-side join, 該功能可以將小表快取的記憶體中,在大表進行Map操作的時候,遍歷記憶體中的小表進行逐一匹配,這樣可以雖然需要將小表分發到各個Map節點,但是卻可以省去Reducer階段
一般的做法是進行如下的設定:
beeline–> set hive.auto.convert.join= true;
來讓hive自動實現,同時可以在hive-site.xml裡面設定對於小表的定義
hive.mapjoin.smalltable.filesize=25000000(單位位元組)
注意:本操作對右連結和全外連線不適合用
(2)從同個表中擷取資料到不同表的優化
(3)order by 與sort by +distribute by
order by 全域性排序,最終會將所有數放到一個ruducer進行排序,資料量大的時候就撲街了
sort by 區域性排序,會在每個reducer內進行排序,在Maper階段完成之後,進行hash操作,將鍵值均勻的分發到Reducer上去,那麼如果我們能保證分發到的每一臺Recuder上的都是同一個key(我們要select的那個值)的話,那麼在每一臺Recuder上進行區域性的排序同樣也能實現order by的功能,這個時候就用distribute by key 來將目標的key分發到不同的Reducer上
3,利用analyse
總的來說,使用analyze table 可以獲取表的一些基本資訊,如
numPartitions,numFiles,totalSize,rawDataSize,numRows,
有助於系統生成更好的查詢計劃。
4,將檔案儲存格式修改為ORC
ORC檔案的概念可以參考這裡
簡單來說ORC是Hive原生的儲存檔案的格式,通過壓縮,索引進一步優化資料儲存加快資料查詢速度。
建立一個以 ORC為格式的檔案如下:
create table if not exists test_orc(
name string,
gender string,
cnt BIGINT
)STORED AS ORC;
或者是壓縮的儲存如下:
create table if not exists test_orc(
name string,
gender string,
cnt BIGINT
)STORED AS ORC tblproperities(“orc.compress”=”SNAPPY”);
其中(“orc.compress”=”SNAPPY”)是設定壓縮的方式,也可以設定為(“orc.compress”=”ZLIB”)
值得注意的是,由於建立的是ORC檔案,所以不能直接的使用load data的cmd將txt檔案插入到資料格式為ORC的表中,只能:
**將資料匯入臨時普通表:
LOAD DATA INPATH ‘/tmp/orc.txt’ OVERWRITE INTO TABLE test_orc_tmp;
將臨時普通表的資料插入到ORC表:
INSERT INTO TABLE test_orc SELECT * FROM test_orc_tmp;**
5,使用VECTORIZATION
關於VECTORIZATION
簡單來說:每次查詢都執行一塊1024行而不是一行,這樣做符合計算機設計底層原理,提高了查詢的速率
設定為:
set hive.vectorized.execution.enabled = true;
set hive.vectorized.execution.reduce.enabled = true;
注意,只要當表的儲存格式為ORC的時候才可以採用這種向量查詢方式。
6,利用並行執行
想hive提交一個job的時候,會根據不同的功能分成不同的stage,可以通過 explain sql 來看,
如果每個stage之間沒有依賴關係的話,可以設定並行執行,可以理解為多執行緒吧,但是這樣會消耗更多資源。
比較好的例子就是union的時候可以並行執行,
設定如下:
// 開啟任務並行執行
set hive.exec.parallel=true;
// 同一個sql允許並行任務的最大執行緒數
set hive.exec.parallel.thread.number=8;
7,利用shell控制資料傾斜
在每天走的ETL的流程中,經常在前面是關聯許多表來形成一個大的資料集,也有一些比較大點的表十幾G和一些上千行的小表進行關聯的操作,由於join 的key分佈不均勻,有些幾十萬次有些幾十次,造成了資料處理緩慢,設定負載均衡也花費了二十多分鐘的時候,影響了整體資料處理的流程。
因為用shell指令碼將該HQL語句進行分層處理,處理後執行時間五分多鐘,加上union各個部分的集合,不到八分鐘。
需要處理的語句如下: 為了方便記錄簡化了語句
hql:
select row_number() over() as row_id ,xxx,xxx,xxx from t_stg00 a,t_rule_main b
where a.balance_sheet_point_1= b.balance_sheet_pointer_lo
and b.balance_sheet_point_lo=b,balance_sheet_point_1_hi
and (a.asset=b.asset or b.asset=’*’)
–and b.row_id between {end_row}; 在這裡控制小表進行join的行數
and a.balance_sheet_pointer_lo in ($joinKeyList); –指定joinKey 的集合,joinKey是造成資料傾斜的資料
接著寫個主要的shell指令碼來處理上面 and b.balance_sheet_pointer_lo in ($joinKeyList);的分佈
logic 是先分析統計join 的key 分佈,並且將分佈儲存在一個file中
格式為 joinKey count
然後遍歷這個file ,如果count>50萬次,則單獨開一個執行緒來執行,如果count<50W,則繼續遍歷下一行並且count的資料繼續增加知道超過50W則再次開執行緒來處理
但這裡有個問題,我們的統計分析是統計分析大表,需要進行count(distinct)的處理,因此會花不少時間,但是由於業務資料的波動範圍不大,因為不用每次都分析資料,只有在資料有必要的時候分析一次並且存入上面所說的要遍歷的file即可。但是這樣子做出來的指令碼和分析思路可以應用到有相似需求的業務。
具體實現:
(1)先對大表進行統計並且存入到一個file中
beeline -u file_path
#統計資料存入檔案中為了下面控制執行緒數量用
(2)
key="key"
count=0
subtaskNum=0
declare -a batch_line ##declare a array named batch_line
while read -ra batchline ## loop the file and read one line and split this line by tab,then put the datas
##array batch_line
key=$batch_line[0]
count=$batch_line[1]
$subtaskNum= `expr $subtaskNum+1`
####
##if match to trigger a proces
####
nohub ./appl/subtask$subtaskNum.sh $joinKeyList >>$log_file 2>&1 &
do
done < $path_of_file ##tell shell which file u want to loop read
note:
用nohup執行命令可以使命令永久的執行下去,和使用者終端沒有關係,例如我們斷開SSH連線都不會影響他的執行,注意了nohup沒有後臺執行的意思;&才是後臺執行
&是指在後臺執行,但當用戶推出(掛起)的時候,命令自動也跟著退出