1. 程式人生 > >Hive優化的十條詳細策略(上)

Hive優化的十條詳細策略(上)

一、Fetch抓取

Fetch 抓取是指,Hive 中對某些情況的查詢可以不必使用 MapReduce 計算。

 在 hive-default.xml.template 檔案中 hive.fetch.task.conversion 預設是 more,老版本 hive
預設是 minimal,該屬性修改為 more  以後,在全域性查詢、欄位查詢、limit  查詢等都不走
mapreduce。

配置檔案如下:
在這裡插入圖片描述
案例一:
1)把 hive.fetch.task.conversion 設定成 none,然後執行查詢語句,都會執行 mapreduce程式。

hive (default)> set hive.fetch.task.conversion=none; 
hive (default)> select * from emp;
hive (default)> select ename from emp;
hive (default)> select ename from emp limit 3;

2)把 hive.fetch.task.conversion 設定成 more,然後執行查詢語句,如下查詢方式都不會執行 mapreduce 程式。

hive (default)> set hive.fetch.task.conversion=more; 
hive (default)> select * from emp;
hive (default)> select ename from emp;
hive (default)> select ename from emp limit 3;

二、本地模式

大多數的Hadoop Job 是需要 Hadoop 提供的完整的可擴充套件性來處理大資料集的。不過, 有時 Hive 的輸入資料量是非常小的。在這種情況下,為查詢觸發執行任務時消耗可能會比實際 job 的執行時間要多的多。對於大多數這種情況,Hive 可以通過本地模式在單臺機器上處理所有的任務。對於小資料集,執行時間可以明顯被縮短。
使用者可以通過設定 hive.exec.mode.local.auto 的值為 true,來讓 Hive 在適當的時候自動啟動這個優化。
在這裡插入圖片描述


案例二:
1)開啟本地模式,並執行查詢語句

hive (default)> set hive.exec.mode.local.auto=true; 
hive (default)> select * from emp cluster by deptno;
 Time taken: 1.328 seconds, Fetched: 14 row(s)

2)關閉本地模式,並執行查詢語句

hive (default)> set hive.exec.mode.local.auto=false; 
hive (default)> select * from emp cluster by deptno; 
Time taken: 20.09 seconds, Fetched: 14 row(s)

三、表的優化

3.1 小表、大表 Join
將 key 相對分散,並且資料量小的表放在 join 的左邊,這樣可以有效減少記憶體溢位錯誤發生的機率;再進一步,可以使用 Group 讓小的維度表(1000 條以下的記錄條數)先進記憶體。在 map 端完成 reduce。
實際測試發現:新版的 hive 已經對小表JOIN 大表和大表JOIN 小表進行了優化。小表放在左邊和右邊已經沒有明顯區別。
案例三:
需求:測試大表 JOIN 小表和小表JOIN 大表的效率
(1)建大表、小表和 JOIN 後表的語句

create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

create table smalltable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

create table jointable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

(2)分別向大表和小表中匯入資料

hive (default)> load data local inpath '/opt/module/datas/bigtable' into table bigtable; 
hive (default)>load data local inpath '/opt/module/datas/smalltable' into table smalltable;

(3)關閉 mapjoin 功能(預設是開啟的

set hive.auto.convert.join = false;

(4)執行小表JOIN 大表語句

insert overwrite table jointable
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url 
from smalltable s
left join bigtable	b
on b.id = s.id;

結果:Time taken: 35.921 seconds
(5)執行大表JOIN 小表語句

insert overwrite table jointable
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url 
from bigtable	b
left join smalltable	s
on s.id = b.id;

結果:Time taken: 34.196 seconds

3.2 大表 Join 大表
1)空 KEY 過濾
有時 join 超時是因為某些 key 對應的資料太多,而相同 key 對應的資料都會發送到相同的 reducer 上,從而導致記憶體不夠。此時我們應該仔細分析這些異常的 key,很多情況下, 這些 key 對應的資料是異常資料,我們需要在 SQL 語句中進行過濾。例如 key 對應的欄位為空,操作如下:
案例四:
(1)配置歷史伺服器
配置 mapred-site.xml
在這裡插入圖片描述
啟動歷史伺服器

sbin/mr-jobhistory-daemon.sh start historyserver

查 看 jobhistory

http://192.168.1.102:19888/jobhistory

(2)建立原始資料表、空 id 表、合併後資料表

create table ori(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

create table nullidtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

create table jointable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

(3)分別載入原始資料和空 id 資料到對應表中

hive (default)> load data local inpath '/opt/module/datas/ori' into table ori;
hive (default)> load data local inpath '/opt/module/datas/nullid' into table nullidtable;

(4)測試不過濾空 id

hive (default)> insert overwrite table jointable
						select n.* from nullidtable n left join ori o on n.id = o.id; 

結果:Time taken: 42.038 seconds
(5)測試過濾空 id

hive (default)> insert overwrite table jointable
select n.* from (select * from nullidtable where id is not null ) n left join ori o on n.id = o.id;

結果:Time taken: 31.725 seconds

2)空 key 轉換
有時雖然某個 key 為空對應的資料很多,但是相應的資料不是異常資料,必須要包含在join 的結果中,此時我們可以表a 中 key 為空的欄位賦一個隨機的值,使得資料隨機均勻地分不到不同的 reducer 上。
案例五:
Ⅰ:不隨機分佈空 null 值:
(1)設定 5 個 reduce 個數

set mapreduce.job.reduces = 5;

(2)JOIN 兩張表

insert overwrite table jointable
select n.* from nullidtable n left join ori b on n.id = b.id;

結果:可以看出來,出現了資料傾斜,某些 reducer 的資源消耗遠大於其他 reducer。
在這裡插入圖片描述
Ⅱ:隨機分佈空 null 值
(1)設定 5 個 reduce 個數

set mapreduce.job.reduces = 5;

(2)JOIN 兩張表

insert overwrite table jointable
select n.* from nullidtable n full join ori o on case when n.id is null then concat('hive', rand()) else n.id end = o.id;

結果:可以看出來,消除了資料傾斜,負載均衡 reducer 的資源消耗
在這裡插入圖片描述

3.3 MapJoin

如果不指定 MapJoin 或者不符合 MapJoin 的條件,那麼 Hive 解析器會將Join 操作轉換成 Common Join,即:在 Reduce 階段完成 join。容易發生資料傾斜。可以用 MapJoin 把小表全部載入到記憶體在 map 端進行 join,避免 reducer 處理。
1)開啟 MapJoin 引數設定:

(1)設定自動選擇 Mapjoin

set hive.auto.convert.join = true; 預設為 true

(2)大表小表的閥值設定(預設 25M 一下認為是小表):

set hive.mapjoin.smalltable.filesize=25000000;

2)MapJoin 工作機制介紹
在這裡插入圖片描述
案例六:
(1)開啟 Mapjoin 功能

set hive.auto.convert.join = true; 預設為 true

(2)執行小表JOIN 大表語句

insert overwrite table jointable
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url from smalltable s
join bigtable	b on s.id = b.id;

結果:Time taken: 24.594 seconds
(3)執行大表JOIN 小表語句

insert overwrite table jointable
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url from bigtable	b
join smalltable	s on s.id = b.id;

結果:Time taken: 24.315 seconds

3.4 Group By
預設情況下,Map 階段同一 Key 資料分發給一個 reduce,當一個 key 資料過大時就傾斜了。並不是所有的聚合操作都需要在 Reduce 端完成,很多聚合操作都可以先在 Map 端進行部分聚合,最後在 Reduce 端得出最終結果。
1)開啟 Map 端聚合引數設定
(1)是否在Map 端進行聚合,預設為

True hive.map.aggr = true

(2)在 Map 端進行聚合操作的條目數目

hive.groupby.mapaggr.checkinterval = 100000

(3)有資料傾斜的時候進行負載均衡(預設是 false)

hive.groupby.skewindata = true

當選項設定為 true,生成的查詢計劃會有兩個 MR Job。
第一個 MR Job 中,Map 的輸出結果會隨機分佈到 Reduce 中,每個 Reduce 做部分聚合操作,並輸出結果,這樣處理的結果是相同的Group By Key 有可能被分發到不同的 Reduce 中,從而達到負載均衡的目的;
第二個 MR Job 再根據預處理的資料結果按照Group By Key 分佈到 Reduce 中(這個過程可以保證相同的Group By Key 被分佈到同一個 Reduce 中),最後完成最終的聚合操作。

3.5 Count(Distinct) 去重統計
資料量小的時候無所謂,資料量大的情況下,由於 COUNT DISTINCT 操作需要用一個Reduce Task 來完成,這一個 Reduce 需要處理的資料量太大,就會導致整個Job 很難完成, 一般 COUNT DISTINCT 使用先 GROUP BY 再 COUNT 的方式替換:
案例七:
(1)建立一張大表

hive (default)> create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string)
 row format delimited fields terminated by '\t';

(2)載入資料

hive (default)> load data local inpath '/opt/module/datas/bigtable' into table bigtable;

(3)設定 5 個 reduce 個數

set mapreduce.job.reduces = 5;

(4)執行去重 id 查詢

hive (default)> select count(distinct id) from bigtable;

結果:
Stage-Stage-1: Map: 1 Reduce: 1 Cumulative CPU: 7.12 sec HDFS Read: 120741990 HDFS Write: 7 SUCCESS
Total MapReduce CPU Time Spent: 7 seconds 120 msec OK
c0
100001
Time taken: 23.607 seconds, Fetched: 1 row(s)
Time taken: 34.941 seconds, Fetched: 1 row(s)
(5)採用GROUP by 去重 id

hive (default)> select count(id) from (select id from bigtable group by id) a;

結果:
Stage-Stage-1: Map: 1 Reduce: 5 Cumulative CPU: 17.53 sec HDFS Read: 120752703 HDFS Write: 580 SUCCESS
Stage-Stage-2: Map: 3 Reduce: 1 Cumulative CPU: 4.29 sec HDFS Read: 9409 HDFS Write: 7 SUCCESS
Total MapReduce CPU Time Spent: 21 seconds 820 msec OK
_c0
100001
Time taken: 50.795 seconds, Fetched: 1 row(s)
雖然會多用一個Job 來完成,但在資料量大的情況下,這個絕對是值得的。

下篇接著總結: