從MapReduce的執行來看如何優化MaxCompute(原ODPS) SQL

摘要: SQL基礎有這些操作(按照執行順序來排列): from join(left join, right join, inner join, outer join ,semi join) where group by select sum distinct count order by 如果我們能理解mapreduce是怎么實現這些SQL中的基本操作的,那么我們將很容易理解怎么優化SQL寫法。

點此查看原文:http://click.aliyun.com/m/41382/

SQL基礎有這些操作(按照執行順序來排列):

from
join(left join, right join, inner join, outer join ,semi join)
where
group by
select
sum
distinct
count
order by
如果我們能理解mapreduce是怎么實現這些SQL中的基本操作的,那么我們將很容易理解怎么優化SQL寫法。接下來我們一個一個的談:

from
這個操作是在解析過程中就完成了,目的就是找出輸入的表(文件)。

join(left join, right join, inner join, outer join ,semi join)
這個操作需要在參與map和reduce整個階段。下圖給出了各個階段的數據輸入輸出變化:
假如執行這個SQL:

select student_id, student_name, course_id 
from student left join student_course on student.student_id = student_course.student_id;

圖片描述

從上面圖可以看出當出現數據在某個(某些)key特別集中的時候,就會出現reduce的接收數據是不均勻的,導致reduce端數據傾斜。

where
這個地方如果有分區字段的話,會直接解析階段就做裁剪。不會拖到后面的map和reduce階段。如果不是分區字段,則只會涉及得到map階段,在這個階段直接過濾。

group by

select student_id, sum(score)
from student_course
group by student_id

將GroupBy的字段組合為map的輸出key值,利用MapReduce的排序,在reduce階段保存LastKey區分不同的key。MapReduce的過程如下(當然這里只是說明Reduce端的非Hash聚合過程)
圖片描述

select

因為MaxComput(原ODPS)的文件存儲是列式的,所以在select在編譯解析的過程中會起到裁剪列的作用。比如一個表假如有100列,select中只出現了3列,那么其余的97列是沒有進行計算的。寫select盡量避免使用*,并且不需要的字段盡量刪減掉。

sum

到這里開始涉及到了聚合函數,聚合函數需要區分可以拆分并行和不可以拆分并行兩種。sum是典型的可拆分并行的。sum(1,2,3,1) = sum(1,2) + sum(3,1) = 7。而avg就是不可并行計算,avg(1,2,3,1) != avg(1,2) + avg(3,1) != avg(avg(1,2) + avg(3,1))。但是avg可以轉化成可并行計算,比如先sum分子,再sum分母來并行化。

如果函數可并行,那么就可以在map階段進行提前聚合,大大減少后面的發往reduce端的網絡傳遞。

distinct

如果是單distinct的話,會把distinct的列直接附在group-by字段組后面,然后進行處理。

麻煩的是multi distinct。根據disinct的邏輯,必須保證每個分組(group-by)相同的distinct列相同的key都分在同一個reduce中,否則就沒有辦法完成去重工作。所以如果按照單distinct的邏輯,reduce端就需要針對每一個distinct字段進行排序和去重。這樣做顯然是不高效的,因為對reduce端的計算壓力很大,而且也沒有利用到shuffle階段的排序。

第二種方法就是把distinct的字段都拆開,形成獨立的n張表。最后再做union all的操作。過程如下:

select date, count(distinct student_id),count(distinct course), sum(score)
from student_course
group by date

圖片描述

order by

在odps上和order by相似的功能在還有sort by, distribute by,cluster by。 后面的語法在普通的關系型數據庫都不存在。算是mapreduce特有的功能。這里先解釋下每個語句的含義:

order by —— order by會對輸入做全局排序,因此只有一個Reducer(多個Reducer無法保證全局有序),然而只有一個Reducer,會導致當輸入規模較大時,消耗較長的計算時間。

sort by —— sort by不是全局排序,其在數據進入reducer前完成排序,因此,如果用sort by進行排序,并且設置mapred.reduce.tasks>1,則sort by只會保證每個reducer的輸出有序,并不保證全局有序。sort by不同于order by,它不受Hive.mapred.mode屬性的影響,sort by的數據只能保證在同一個reduce中的數據可以按指定字段排序。使用sort by你可以指定執行的reduce個數(通過set mapred.reduce.tasks=n來指定),對輸出的數據再執行歸并排序,即可得到全部結果。

distribute by —— distribute by是控制在map端如何拆分數據給reduce端的。hive會根據distribute by后面列,對應reduce的個數進行分發,默認是采用hash算法。sort by為每個reduce產生一個排序文件。在有些情況下,你需要控制某個特定行應該到哪個reducer,這通常是為了進行后續的聚集操作。distribute by剛好可以做這件事。因此,distribute by經常和sort by配合使用。

cluster by —— cluster by除了具有distribute by的功能外還兼具sort by的功能。但是排序只能是倒敘排序,不能指定排序規則為ASC或者DESC。

MapReduce的幾個階段

input
split
map
shuffle
reduce
output 這每個階段都會出現各種問題,我們依次從前到后來講怎么處理各個階段出現的問題。

Input & split

根據MaxCompute的功能,input可以是本地文件,也可以是數據庫的表。可以通過InputFormat借口來定義。但是這個Format和后面的split階段息息相關。因為split只切割比block小的文件,對于小文件則不作處理。所以當存在大量的小文件(特指大小達不到block大小的文件),會生成大量的split塊,同時也會啟動大量map任務。

可能出現的問題

分區裁剪中出現問題 > 解決方法是讓odps在生成任務之前就能確定好讀區到分區的范圍
輸入存在大量小文件,導致map instance數量超標 > 解決辦法是讀取時候設定塊大小,可以使用setSplitSize來控制讀取文件總大小 > 解決方案二是提前就把這些小文件給合并了
輸入文件大小分布非常不均勻,導致split的塊大小分布不均勻,從而導致map端傾斜 > 可以使用setSplitSize來控制讀取文件總大小
輸入的文件不能被切割,導致split塊大小不均勻

暫時沒有找到解法

相比于hadoop,odps系統在小文件處理方面的功能已經比較完善,主要體現在以下兩個方面:
(1) 默認情況下,當Job完成之后,如果滿足一定的條件,系統會自動分配一個FuxiTask(調度任務)進行小文件合并,即我們經常看到的MergeTask;

map

map階段的輸入是上面Input&split階段來保障的,一個分片一個map任務。所以當分片處理的不合理,map階段就會出現問題。而map端經過shuffle和combianer(可選)后,會把數據交給reduce端。

從input&split 到map可能出現的問題
輸入存在大量小文件,導致map instance數量超標 > 同上
因為ODPS的SQL或者其他任務會解析成一個Task DAG。所以從最初輸入到最終輸出會有很多的中間計算。而這些中間計算之間也是對應著一個個的map reduce。如果當上一個map/reduce任務產生的輸入可能形成一個種長尾分布,導致下一個mapreduce輸入出現長尾。也就是map端任務傾斜。

shuffle

這個階段是mapreduce的核心,設計到sort,group和數據分發。

可能出現的問題
數據量特別大,可以使用combinar來進行mapper端的聚合。odps的參數是

reduce

知道mapreduce計算模型的人都知道,map階段輸入是非結構化的,并不需要實現規定好輸入的內容,輸出則是一塊塊分區好的pair。而到reduce則有要求,那就是同樣key的map處理的pair需要發送到同樣的reduce中。這樣就會出現某key數據量很大,某key數據量很小的時候對應的reduce處理的數據量大小也是不均勻的。一旦出現這種情,任務執行的結束時間必然會受到最長任務的拖累。,v>,v>

能產生reduce數據分布不均勻的操作,最長出現的有兩分類:

  1. join

這里推薦本書《mapreduce設計模式》,其中的連接模式篇章把各種join的描述。在這里大概說下join的類型:

reduce端連接
map端連接(在odps中使用mapjoin即可),這個操作的前提是存在一個小表能放入到mapreduce中的環形內存中。而且大表必須作為“主表”(比如left join的話就必須是左表,而right join就必須是右表)。
所以到底為什么會產生傾斜呢?map端連接肯定是不會產生數據傾斜的,那么傾斜的必然是reduce連接。當一張表出現數據熱點。這樣就會出現熱點reduce的運行遠遠大于其它的長尾,導致數據不均衡。

大概總結下就是:

  • 如果存在小表,且如果左外連接時候小表是右表(或者是右外連接,小表必須是左表),可以使用mapjoin。
  • 如果都是大表且有熱點,這樣會出現傾斜,這時候需要剔除熱點數據單獨處理。
  • 如果都是大表沒有熱點,這樣不會出現傾斜,這樣還需要怎么優化?——這里首選想辦法減小數據集合,如果不能在查看是否出現某些熱門的數據,如果有,則對數據進行分桶。

count(distinct) 對于distinct的實現,單鍵的時候會被直接附到group by的字段后,同時作為map輸出的key值來處理。這樣轉化成了group by處理,一般是沒有問題的。但是麻煩的是多鍵值count(distinct),這個沒有辦法直接把所有的distinct的字段附到group by后面了。因為這樣無法利用shuffle階段的排序,到了reduce階段需要做很多遍的去重操作。所有一般對于multi distinct都是采用給distinct 字段做編號,然后復制數據。比如輸入數據是這樣:
可以看到distinct會導致數據翻倍膨脹,而這些膨脹的數據后會通過網絡傳輸到reduce,必然會造成很大的浪費。所以要治理,方法一是首先把distinct轉成group by放在子查詢中,然后外層再套一層查詢進行分組count。

select user_id,count(deal_id),count(item) 
from
(select  user_id,deal_id, item from deal_list group by user_id,deal_id, item
) group by user_id;

方法二:設置參數——odps.sql.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 中),最后完成最終的聚合操作。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/452760.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/452760.shtml
英文地址,請注明出處:http://en.pswp.cn/news/452760.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

套接字(socket)基本知識與工作原理

套接字(socket)基本知識與工作原理 一、Socket相關概念 Socket通常也稱作“套接字”,用于描述IP地址和端口,是一個通信鏈的句柄。(其實就是兩個程序通信用的。) SOCKET用于在兩個基于TCP/IP協議的應用程序之…

python 多線程--重點知識

1.全局變量global的用法 2.多線程共享全局變量-args參數 注意args參數類型為元組,逗號不能少!

Flask WTForm表單的使用

運行環境: python2.7 flask 0.11 flask-wtf 0.14.2 wtform能夠通過一個類定義一些字段,這些字段會在前端生成標簽,并且通過設置字段的驗證規則,自動判斷前端輸入數據的格式。 一般用于用戶登錄,用戶注冊等信息錄入。…

Java與C#個人之比較

網上這方面的比較文章已經有不少了,不過大都是要么從很高的角度說的,要么就是從底層說的,本人就以自己這幾年的編程經歷中的感受,來談談自己的體會。 相似性: Java和C#都是一門面向對象的語言,Java更多地…

java利用子類求正方形_Java程序設計實驗2011

(2)掌握對象的聲明和使用;(3)掌握構造方法的概念和使用;(4)掌握類及成員的訪問控制符。2、實驗任務(1)閱讀下面的程序,在main()方法里添加語句完成如下的功能:①創建一個MyV alue類的對象myV alue。②為myV alue對象中的value域賦…

當導用模塊與包的import與from的問題(模塊與包的調用)

當在views.py里寫impor models會不會報錯呢? 1、Python里面的py文件都是每一行的代碼。2、Python解釋器去找一個模塊的時候,只去sys.path的路徑里找3、django項目啟動(django項目的啟動文件是manage.py)啟動項目是將manage.py的路…

ack和seq

ACK (Acknowledgement),即確認字符,在數據通信中,接收站發給發送站的一種傳輸類控制字符。表示發來的數據已確認接收無誤。 seq是序列號,這是為了連接以后傳送數據用的,ack是對收到的數據包的確認&#xff…

MySQL中的information_schema

0.引言 近日在學習網絡安全的sql注入時,用到mysql中的information_schema數據庫,其思路是利用information_schema中的SCHEMA獲取數據庫中的table名稱。現在對相關數據庫進行總結,方便以后復習使用。 2.information_schema數據庫 informati…

linux配置防火墻,開啟端口

linux配置防火墻,開啟端口 Centos7,配置防火墻,開啟端口  1.查看已開放的端口(默認不開放任何端口)    firewall-cmd --list-ports  2.開啟80端口    firewall-cmd --zonepublic(作用域) --add-port80/tcp(端口和訪問類型) --permanent(永久…

使用Intel編譯器系列合集

好的帖子:http://topic.csdn.net/u/20080327/16/071b45df-3795-4bf1-9c4d-da4eb5aaa739.html參考手冊:http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011Update/compiler_c/index.htm 說明:本系列文章為個…

【前端】這可能是你看過最全的css居中解決方案了~

1.水平居中&#xff1a;行內元素解決方案 適用元素&#xff1a;文字&#xff0c;鏈接&#xff0c;及其其它inline或者inline-*類型元素&#xff08;inline-block&#xff0c;inline-table&#xff0c;inline-flex&#xff09; html部分代碼:<div>文字元素</div><…

java手機一款三國游戲_JAVA熱游—富甲三國之雄霸天下原創心得

因為工作忙碌的關系&#xff0c;很長時間都沒有來關注手機游戲論壇&#xff0c;這款富甲三國.雄霸天下&#xff0c;我也是前天才拿到手。游戲比想象中的簡單&#xff0c;個人僅用了兩個小時時間&#xff0c;就將三個人物全部通關。游戲的開始畫面制作得比較精美&#xff0c;而且…

Python多線程--互斥鎖、死鎖

1、互斥鎖 為解決資源搶奪問題&#xff0c;使用mutex Threading.Lock()創建鎖&#xff0c;使用mutex.acquire()鎖定&#xff0c;使用mutex.release()釋放鎖。 代碼一&#xff1a; import threading import time# 定義一個全局變量 g_num 0def test1(num):global g_num# 上鎖…

freemind 要下載java_Freemind

動手編輯先按Ctrln&#xff0c;新建一個文件。這時出現了一個根節點。用光標單擊它&#xff0c;改成“我學FreeMind”&#xff0c;然后在節點之外任一地方點擊鼠標(或按Enter)完成編輯。然后&#xff0c;按Insert鍵&#xff0c;輸入“下載安裝”&#xff0c;按Enter鍵&#xff…

本地連不上遠程mysql數據庫(2)

Host is not allowed to connect to this MySQL server解決方法 今天在ubuntu上面裝完MySQL&#xff0c;卻發現在本地登錄可以&#xff0c;但是遠程登錄卻報錯Host is not allowed to connect to this MySQL server,找了半天試了網上的一些方法都沒有解決&#xff0c;最終在一篇…

理解EnterCriticalSection 臨界區

通俗解釋就像上廁所&#xff1a; 門鎖了&#xff0c;就等著&#xff0c;等到別人出來了&#xff0c;進去鎖上&#xff0c;然后該干什么干什么&#xff0c;干完了&#xff0c;把門打開 門沒鎖&#xff0c;就進去&#xff0c;鎖上&#xff0c;然后該干什么干什么&#xff0c;干…

Python多線程--UDP聊天器

import socket import threadingdef recv_msg(udp_socket):"""接收數據并顯示"""# 接收數據while True:recv_data udp_socket.recvfrom(1024)print(recv_data)def send_msg(udp_socket, dest_ip, dest_port):"""發送數據"&…

mvc:default-servlet-handler/作用

<mvc:default-servlet-handler/>使用默認的servlet來相應靜態文件&#xff0c;因為在web.xml中使用了DispatcherServlet截獲所有的請求url&#xff0c;而引入<scprit type"text/javascript" src"js/jquery-1.11.0.mim.js"/>的時候&#xff0c;…

java中如何做模糊查詢_到底Java里的模糊查詢語句該怎么寫

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓現在String sql"select * from car where carName like %?%";可以查詢出結果了&#xff0c;但問題又來了&#xff0c;只能查詢出一條結果&#xff0c;代碼如下carDao.java頁面public ArrayList queryAppoint(String car…