map join優化

轉載:http://www.gemini5201314.net/hadoop/hadoop-%E4%B8%AD%E7%9A%84%E4%B8%A4%E8%A1%A8join.html

爲數據分析中經常進行的join 操作,傳統DBMS 數據庫已經將各種算法優化到了極致,而對於hadoop 使用的mapreduce 所進行的join 操作,去年開始也是有各種不同的算法論文出現,討論各種算法的適用場景和取捨條件,本文討論hive 中出現的幾種join 優化,然後討論其他算法實現,希望能給使用hadoop 做數據分析的開發人員提供一點幫助.

 

Facebook 今年在yahoo 的hadoop summit 大會上做了一個關於最近兩個版本的hive 上所做的一些join優化,其中主要涉及到hive 的幾個關鍵特性: 值分區 , hash 分區 , map join , index ,

 

Common Join

最爲普通的join策略,不受數據量的大小影響,也可以叫做reduce side join ,最沒效率的一種join 方式. 它由一個mapreduce job 完成.

首先將大表和小表分別進行map 操作, 在map shuffle 的階段每一個map output key 變成了table_name_tag_prefix + join_column_value , 但是在進行partition 的時候它仍然只使用join_column_value 進行hash.

每一個reduce 接受所有的map 傳過來的split , 在reducce 的shuffle 階段,它將map output key 前面的table_name_tag_prefix 給捨棄掉進行比較. 因爲reduce 的個數可以由小表的大小進行決定,所以對於每一個節點的reduce 一定可以將小表的split 放入內存變成hashtable. 然後將大表的每一條記錄進行一條一條的比較.

Map Join

Map Join 的計算步驟分兩步,將小表的數據變成hashtable廣播到所有的map 端,將大表的數據進行合理的切分,然後在map 階段的時候用大表的數據一行一行的去探測(probe) 小表的hashtable. 如果join key 相等,就寫入HDFS.

map join 之所以叫做map join 是因爲它所有的工作都在map 端進行計算.

hive 在map join 上做了幾個優化

hive 0.6 的時候默認認爲寫在select 後面的是大表,前面的是小表, 或者使用 /*+mapjoin(map_table) */ 提示進行設定. hive 0.7 的時候這個計算是自動化的,它首先會自動判斷哪個是小表,哪個是大表,這個參數由(hive.auto.convert.join=true)來控制. 然後控制小表的大小由(hive.smalltable.filesize=25000000L)參數控制(默認是25M),當小表超過這個大小,hive 會默認轉化成common join. 你可以查看HIVE-1642. 首先小表的Map 階段它會將自己轉化成MapReduce Local Task ,然後從HDFS 取小表的所有數據,將自己轉化成Hashtable file 並壓縮打包放入DistributedCache 裏面.

目前hive 的map join 有幾個限制,一個是它打算用BloomFilter 來實現hashtable , BloomFilter 大概比hashtable 省8-10倍的內存, 但是BloomFilter 的大小比較難控制.

現在DistributedCache 裏面hashtable默認的複製是3份,對於一個有1000個map 的大表來說,這個數字太小,大多數map 操作都等着DistributedCache 複製.

 

Bucket Map Join

hive 建表的時候支持hash 分區通過指定clustered by (col_name,xxx ) into number_buckets buckets 關鍵字.

當連接的兩個表的join key 就是bucket column 的時候,就可以通過

hive.optimize.bucketmapjoin= true

來控制hive 執行bucket map join 了, 需要注意的是你的小表的number_buckets 必須是大表的倍數. 無論多少個表進行連接這個條件都必須滿足.(其實如果都按照2的指數倍來分bucket, 大表也可以是小表的倍數,不過這中間需要多計算一次,對int 有效,long 和string 不清楚)

Bucket Map Join 執行計劃分兩步,第一步先將小表做map 操作變成hashtable 然後廣播到所有大表的map端,大表的map端接受了number_buckets 個小表的hashtable並不需要合成一個大的hashtable,直接可以進行map 操作,map 操作會產生number_buckets 個split,每個split 的標記跟小表的hashtable 標記是一樣的, 在執行projection 操作的時候,只需要將小表的一個hashtable 放入內存即可,然後將大表的對應的split 拿出來進行判斷,所以其內存限制爲小表中最大的那個hashtable 的大小.

Bucket Map Join 同時也是Map Side Join 的一種實現,所有計算都在Map 端完成,沒有Reduce 的都被叫做Map Side Join ,Bucket 只是hive 的一種hash partition 的實現,另外一種當然是值分區.

create table a  (xxx) partition by (col_name)

不過一般hive 中兩個表不一定會有同一個partition key, 即使有也不一定會是join key. 所以hive 沒有這種基於值的map side join, hive 中的list partition 主要是用來過濾數據的而不是分區. 兩個主要參數爲(hive.optimize.cp = true 和 hive.optimize.pruner=true)

hadoop 源代碼中默認提供map side join 的實現, 你可以在hadoop 源碼的src/contrib/data_join/src 目錄下找到相關的幾個類.  其中TaggedMapOutput 即可以用來實現hash 也可以實現list , 看你自己決定怎麼分區. Hadoop Definitive Guide 第8章關於map side join 和side data distribution 章節也有一個例子示例怎樣實現值分區的map side join.

 

Sort Merge Bucket Map Join

Bucket Map Join 並沒有解決map join 在小表必須完全裝載進內存的限制, 如果想要在一個reduce 節點的大表和小表都不用裝載進內存,必須使兩個表都在join key 上有序纔行,你可以在建表的時候就指定sorted byjoin key 或者使用index 的方式.

set hive.optimize.bucketmapjoin = true;

set hive.optimize.bucketmapjoin.sortedmerge = true;

set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;

Bucket columns == Join columns == sort columns

這樣小表的數據可以每次只讀取一部分,然後還是用大表一行一行的去匹配,這樣的join 沒有限制內存的大小. 並且也可以執行全外連接.

 

Skew Join

真實數據中數據傾斜是一定的, hadoop 中默認是使用

hive.exec.reducers.bytes.per.reducer = 1000000000

也就是每個節點的reduce 默認是處理1G大小的數據,如果你的join 操作也產生了數據傾斜,那麼你可以在hive 中設定

set hive.optimize.skewjoin = true; 
set hive.skewjoin.key = skew_key_threshold (default = 100000)

hive 在運行的時候沒有辦法判斷哪個key 會產生多大的傾斜,所以使用這個參數控制傾斜的閾值,如果超過這個值,新的值會發送給那些還沒有達到的reduce, 一般可以設置成你

(處理的總記錄數/reduce個數)的2-4倍都可以接受.

傾斜是經常會存在的,一般select 的層數超過2層,翻譯成執行計劃多於3個以上的mapreduce job 都很容易產生傾斜,建議每次運行比較複雜的sql 之前都可以設一下這個參數. 如果你不知道設置多少,可以就按官方默認的1個reduce 只處理1G 的算法,那麼  skew_key_threshold  = 1G/平均行長. 或者默認直接設成250000000 (差不多算平均行長4個字節)

 

Left Semi Join

hive 中沒有in/exist 這樣的子句,所以需要將這種類型的子句轉成left semi join. left semi join 是隻傳遞表的join key給map 階段 , 如果key 足夠小還是執行map join, 如果不是則還是common join.

 

 

join 策略中的難點

大多數只適合等值連接(equal join) ,

範圍比較和全外連接沒有合適的支持

提前分區,零時分區,排序,多種不同執行計劃很難評價最優方案.

沒有考慮IO 比如臨時表,網絡消耗和網絡延遲時間,CPU時間,

最優的方案不代表系統資源消耗最少.

 

 

參考資料:

Join Strategy in Hive

https://cwiki.apache.org/confluence/display/Hive/Presentations

 

Join Optimization

https://cwiki.apache.org/Hive/joinoptimization.html


另外taobao 博客上以前的文章對hive -0.6版本有效。其他相關博客文章見以下鏈接


http://www.tbdata.org/archives/595  淘寶博客

http://dongxicheng.org/mapreduce/hadoop-join-two-tables/   懂的博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章