阿裏雲大數據利器Maxcompute-使用mapjoin優化查詢
大數據計算服務(MaxCompute,原名 ODPS)是一種快速、完全托管的 GB/TB/PB 級數據倉庫解決方案。
https://help.aliyun.com/document_detail/27800.html?spm=5176.7840267.6.539.po3IvS
主要有三種操作數據的方式SQL,UDF,MapReduce,了解hadoop的同學就比較熟悉這些東西了。
那麼Maxcompute的SQL和標準SQL最大的區別就是在Maxcompute中SQL會被解析成MapReduce去執行,當然也可以直接去寫MapReduce去計算數據,UDF就是當自帶的一些sql引用的函數不能滿足業務計算的時候,自己通過代碼編寫一個函數,sql執行的時候引用。
由此可見實際上底層的計算都是依靠MapReduce這個計算引擎去執行。首先了解下什麼是MapReduce。一份數據很大的時候在MaxCompute上是分布式存儲的,也就是會分開存放到很多服務器,當一個任務執行的時候會從這些數據所在的服務器上啟動一個進程讀取這些數據,進行計算等操作,還會啟動一個進程把這些數據進行匯總分析並輸出。那前者進程叫做Map,後者進程叫做Reduce,合起來叫MapReduce任務。
使用sql操作數據的時候,會經常用到join。比如select * from A a join B b on a.id=b.id,這句sql在轉換成MapReduce任務執行的時候:
1,map任務讀數據,並對兩個表的數據打上不同的tag用來區分
2,reduce端接收打標記的數據,將不同標記的表數據相同關聯字段的數據
首先有兩個表,我們暫且叫做Big表和Small表,其中Big表數據量比較大,分布式存在n台實例服務器上,Small表存在於一台服務器就放下了。
首選MaxCompute會啟動一些Map的進程(Map任務)去讀取這些數據分別打上標記,Map的個數是由一個參數控製的這裏暫時不解釋了。注意對於讀取Big表的每個Map任務有可能在其他服務器上,那麼這時候就需要到數據所在的服務器上把數據拉過來,Small表也會啟動一個或者幾個map任務讀取文件係統中的數據,讀取完成後會到Reduce端接收數據進行關聯,判斷關聯字段相等的就放在一起輸出,達到關聯效果。
我們可以看一個例子,我準備了一個相對大的表train_user_lt,5G大小,數據大概7億條。
準備了一個比較小的表map_join_test,隻有3條數據。
select a.* from train_user_lt a left outer join map_join_test b on a.user_id = b.user_id;
執行了這句sql,如圖
這個執行的過程圖是Maxcompute特有的可以幫助用戶來查看任務執行的過程等叫做logview,是一個在ODPS Job提交後查看和Debug任務的工具https://help.aliyun.com/document_detail/27987.html
從圖中可以看出分為三部分
1,大的表train_user_lt啟動了39個map任務去讀取數據707025259條
2,小的表啟動一個map任務讀取3條數據。
3,reduce階段接收了3+707025259=707025262條數據,輸出了707025259條數據,left outer join按照左邊的大表輸出。
但是看下消耗的時間是40分鍾,這樣來說算是很長的時間的。那麼怎麼優化提高速度呢,有沒有一種比較方便,比較直接暴力的方式進行優化呢
那麼本文的重點就來了--Mapjoin:
MAPJION會把小表全部讀入內存中,把小表拷貝多份分發到大表數據所在實例上的內存裏,在map階段直接拿另外一個表的數據和內存中表數據做匹配,由於在map是進行了join操作,省去了reduce運行的效率會高很多。
使用的條件就是當一個大表和一個或多個小表做join時。SQL會將用戶指定的小表全部加載到執行join操作的程序的內存中,從而加快join的執行速度。需要注意,在Maxcompute使用mapjoin時:
left outer join的左表必須是大表;
right outer join的右表必須是大表;
inner join左表或右表均可以作為大表;
full outer join不能使用mapjoin;
mapjoin支持小表為子查詢;
使用mapjoin時需要引用小表或是子查詢時,需要引用別名;
在mapjoin中,可以使用不等值連接或者使用or連接多個條件;
目前MaxCompute 在mapjoin中最多支持指定8張小表,否則報語法錯誤;
如果使用mapjoin,則所有小表占用的內存總和不得超過512MB。請注意由於MaxCompute 是壓縮存儲,因此小表在被加載到內存後,數據大小會急劇膨脹。此處的512MB限製是加載到內存後的空間大小;
多個表join時,最左邊的兩個表不能同時是mapjoin的表。
那麼為什麼說left outer join的左表必須是大表呢,
因為左表是大表的時候,會拿小表的全部數據和大表所在的實例服務器中的數據匹配一遍,剛好小表就在內存裏。如果是左表是小表,那麼需要把大表所有的數據拉過來跟小表匹配一遍,試想一下性能會如何。
來看下寫法
select /* + mapjoin(b) */ a.* from train_user_lt a left outer join map_join_test b on a.user_id = b.user_id;
//就是在sql語句前加一個標記說這是mapjoin,把小表別名寫在括號裏
看下優化後的效果
任務變成了兩個部分,map端直接讀取數據和內存裏的小表進行關聯,然後輸出,少了一步reduce。也就是說關聯從reduce轉到map端進行join,省去了reduce這一步,所以叫做:mapjoin。
看下執行時間1分鍾20多秒。之前是40分鍾。當然我這邊測試是把兩個比較極端的數據進行比較,所以效果比較明顯。由此看來大表關聯小表的時候可以使用mapjoin進行優化查詢。
那麼mapjoin除了優化性能,還可以幹什麼呢。
MaxCompute SQL不支持支持在普通join的on條件中使用不等值表達式、or ,like等邏輯等複雜的join條件,但是在mapjoin中可以進行如上操作。例如
select /*+ mapjoin(a) */
a.total_price,
b.total_price
from shop a join sale_detail b
on a.total_price < b.total_price or a.total_price + b.total_price < 500;
總結:mapjoin看似很小的操作變化,實際上可以帶來很大效率提升,另外還可以解決一些不等關聯的業務場景。
正如馬雲經常說的一句話:small is beautiful,small is powerful
最後更新:2017-07-12 23:02:23