hive優化(一)

要點:

優化時,把 hive sql 當做 map reduce 程序來讀,會有意想不到的驚喜。
理解 hadoop 的核心能力,是 hive 優化的根本。
長期觀察 hadoop 處理數據的過程,有幾個顯著的特徵:

1.不怕數據多,就怕數據傾斜。
2.對 jobs 數比較多的作業運行效率相對比較低,比如即使有幾百行的表,如果多次關聯
多次彙總,產生十幾個 jobs,沒半小時是跑不完的。map reduce 作業初始化的時間是比
較長的。
3.對 sum,count 來說,不存在數據傾斜問題。
4.對 count(distinct ),效率較低,數據量一多,準出問題,如果是多 count(distinct )效率更
低。

優化可以從幾個方面着手:

1. 好的模型設計事半功倍。
2. 解決數據傾斜問題。
3. 減少 job 數。
4. 設置合理的 map reduce 的 task 數,能有效提升性能。(比如,10w+級別的計算,用
160 個 reduce,那是相當的浪費,1 個足夠)。
5. 自己動手寫 sql 解決數據傾斜問題是個不錯的選擇。set hive.groupby.skewindata=true;
這是通用的算法優化,但算法優化總是漠視業務,習慣性提供通用的解決方法。 Etl 開發
人員更瞭解業務,更瞭解數據,所以通過業務邏輯解決傾斜的方法往往更精確,更有效。
6. 對 count(distinct)採取漠視的方法,尤其數據大的時候很容易產生傾斜問題,不抱僥倖
心理。自己動手,豐衣足食。
7. 對小文件進行合併,是行至有效的提高調度效率的方法,假如我們的作業設置合理的文
件數,對雲梯的整體調度效率也會產生積極的影響。
8. 優化時把握整體,單個作業最優不如整體最優。

優化案例:

問題 1:如日誌中,常會有信息丟失的問題,比如全網日誌中的 user_id,如果取其中的
user_id 和 bmw_users 關聯,就會碰到數據傾斜的問題。

解決方法 1. User_id 爲空的不參與關聯,例如:
Select *
From log a
Join bmw_users b
On a.user_id is not null
And a.user_id = b.user_id
Union all
Select *
from log a
where a.user_id is null.

解決方法 2 :
Select *
from log a
left outer join bmw_users b
on case when a.user_id is null then concat(‘dp_hive’,rand() ) else a.user_id end = b.user_id;

總結:2 比 1 效率更好,不但 io 少了,而且作業數也少了。1 方法 log 讀取兩次,jobs 是
2。2 方法 job 數是 1 。這個優化適合無效 id(比如-99,’’,null 等)產生的傾斜問題。把空值
的 key 變成一個字符串加上隨機數,就能把傾斜的數據分到不同的 reduce 上 ,解決數據傾
斜問題。因爲空值不參與關聯,即使分到不同的 reduce 上,也不影響最終的結果。附上
hadoop 通用關聯的實現方法(關聯通過二次排序實現的,關聯的列爲 parition key,關聯的
列 c1 和表的 tag 組成排序的 group key,根據 parition key 分配 reduce。同一 reduce 內根
據 group key 排序)。

問題 2:不同數據類型 id 的關聯會產生數據傾斜問題。

一張表 s8 的日誌,每個商品一條記錄,要和商品表關聯。但關聯卻碰到傾斜的問題。s8
的日誌中有字符串商品 id,也有數字的商品 id,類型是 string 的,但商品中的數字 id 是 bigint
的。猜測問題的原因是把 s8 的商品 id 轉成數字 id 做 hash 來分配 reduce,所以字符串 id
的 s8 日誌,都到一個 reduce 上了,解決的方法驗證了這個猜測。

方法:把數字類型轉換成字符串類型
Select * from s8_log a
Left outer join r_auction_auctions b
On a.auction_id = cast(b.auction_id as string);

問題 3:利用 hive 對 UNION ALL 的優化的特性
hive 對 union all 優化只侷限於非嵌套查詢。

比如以下的例子:
select * from
(select * from t1
Group by c1,c2,c3
Union all
Select * from t2
Group by c1,c2,c3) t3
Group by c1,c2,c3;
從業務邏輯上說,子查詢內的 group by 怎麼都看顯得多餘(功能上的多餘,除非有
count(distinct)),如果不是因爲 hive bug 或者性能上的考量(曾經出現如果不子查詢
group by ,數據得不到正確的結果的 hive bug)。所以這個 hive 按經驗轉換成
select * from
(select * from t1
Union all
Select * from t2
) t3
Group by c1,c2,c3;
經過測試,並未出現 union all 的 hive bug,數據是一致的。mr 的作業數有 3 減少到 1。
t1 相當於一個目錄,t2 相當於一個目錄,那麼對 map reduce 程序來說,t1,t2 可以做爲
map reduce 作業的 mutli inputs。那麼,這可以通過一個 map reduce 來解決這個問題。
Hadoop 的計算框架,不怕數據多,就怕作業數多。
但如果換成是其他計算平臺如 oracle,那就不一定了,因爲把大的輸入拆成兩個輸入,分
別排序彙總後 merge(假如兩個子排序是並行的話),是有可能性能更優的(比如希爾排序
比冒泡排序的性能更優)。

問題 4:比如推廣效果表要和商品表關聯,效果表中的 auction id 列既有商品 id,也有數
字 id,和商品表關聯得到商品的信息。那麼以下的 hive sql 性能會比較好

Select * from effect a
Join (select auction_id as auction_id from auctions
Union all
Select auction_string_id as auction_id from auctions
) b
On a.auction_id = b.auction_id。
比分別過濾數字 id,字符串 id 然後分別和商品表關聯性能要好。
這樣寫的好處,1 個 MR 作業,商品表只讀取一次,推廣效果表只讀取一次。把這個 sql 換成
MR 代碼的話,map 的時候,把 a 表的記錄打上標籤 a,商品表記錄每讀取一條,打上標籤
b,變成兩個<key ,value>對,<b,數字 id>,<b,字符串 id>。所以商品表的 hdfs 讀只會是
一次。

問題 5:先 join 生成臨時表,在 union all 還是寫嵌套查詢,這是個問題。比如以下例
子:

Select *
From (select *
From t1
Uion all
select *
From t4Union all
Select *
From t2
Join t3
On t2.id = t3.id
) x
Group by c1,c2;
這個會有 4 個 jobs。假如先 join 生成臨時表的話 t5,然後 union all,會變成 2 個 jobs。
Insert overwrite table t5
Select *
From t2
Join t3
On t2.id = t3.id
;
Select * from (t1 union all t4 union all t5) ;
hive 在 union all 優化上可以做得更智能(把子查詢當做臨時表),這樣可以減少開發人
員的負擔。出現這個問題的原因應該是 union all 目前的優化只侷限於非嵌套查詢。如果
寫 MR 程序這一點也不是問題,就是 multi inputs。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章