An intermediate data placement algorithm for load balancing in Spark computing environment
最近在研究一些Spark成本優(yōu)化的東西,看了一些論文稍微總結(jié)一下思路,方便思維拓寬和希望與大家交流!
本篇博文參考自:
Future Generation Computer Systems 78 (2018) 287–301:
《An intermediate data placement algorithm for load balancing in Spark computing environment》
1 文章概述及問題描述
Spark作為基于內(nèi)存迭代的云計(jì)算框架,其很容易發(fā)生數(shù)據(jù)傾斜,尤其是在Shuffle階段,reduce端所拉取的數(shù)據(jù)量很容易出現(xiàn)不平衡,這將導(dǎo)致某些reduce計(jì)算很久,使得整體計(jì)算發(fā)生延時(shí),嚴(yán)重時(shí)會(huì)導(dǎo)致application失敗。本篇論文討論spark中mapreduce框架中所出現(xiàn)的shuffle階段的數(shù)據(jù)傾斜問題。
MR的數(shù)據(jù)格式全是K-V形式,因此數(shù)據(jù)的傾斜就是Key的傾斜,導(dǎo)致某些分區(qū)中的數(shù)據(jù)量過大,因此,對(duì)于reducer來說,partition的傾斜將會(huì)導(dǎo)致reduce的傾斜。
- 為什么容易出現(xiàn)數(shù)據(jù)傾斜 *
partition的大小取決于具有相關(guān)性的key/value的數(shù)量,由于spark中keys的分配調(diào)取由hash算法決定,因此不同的reducer之間會(huì)出現(xiàn)數(shù)據(jù)量不同的情況,有些reducer要處理的數(shù)據(jù)可能會(huì)非常大。
2 論文中的相關(guān)術(shù)語以及spark基礎(chǔ)
-
clusters
一個(gè)data clusters就是一組包含全部相同Key的map中間輸出結(jié)果的數(shù)據(jù)集合。
在Spark中,所有的被相同的reduce處理的clustes組成一個(gè)分區(qū)(partition)。
-
Bucket
bucket是指一個(gè)序列的緩沖區(qū),用于收集并緩存map的輸出,相對(duì)應(yīng)的reduce會(huì)從相應(yīng)的bucket上拉取數(shù)據(jù)。
-
MR*
假設(shè)reduce個(gè)數(shù)為R,map個(gè)數(shù)為M,則每一個(gè)map將會(huì)創(chuàng)建R個(gè)bucket,所以bucket的總數(shù)為R*M
-
spark-shuffle(1.1.0)中的輸出分布
Data distribution of shuffle in Spark 1.1.0.
-
Zipf distributions
文本數(shù)據(jù)的key一般來說是服從Zipf distributions
參考:J. Lin, et al. The curse of zipf and limits to parallelization: A look at the stragglers problem in mapreduce, in: 7th Workshop on Large-Scale Distributed Systems for Information Retrieval, 2012, pp. 2000–2009.
3 方法概述
為了解決shuffle階段出現(xiàn)的數(shù)據(jù)傾斜,本文提出了一種分割與合并的算法(splitting and combination algorithm for skew intermediate data blocks (SCID)),這種算法采用基于水塘抽樣的抽樣算法,來自動(dòng)偵測(cè)中間數(shù)據(jù)的分布情況,并且是在spark程序運(yùn)行之前就可以判斷。
大致做法就是SCID會(huì)對(duì)鍵值集合的clustes的大小進(jìn)行排序,并且將會(huì)依次存放在相應(yīng)的bucket中,bucket的大小是固定的,某個(gè)bucket存滿后,該clustes會(huì)被切分,剩下的clusters將進(jìn)入下一次的迭代。經(jīng)過這樣的幾輪分配,每一個(gè)bucket中的數(shù)據(jù)大小幾乎相同,所以此時(shí)相應(yīng)的reducer拉取到的數(shù)據(jù)就是相同的。與此同時(shí),我們可以知道,同一個(gè)bucket中可能會(huì)存放來自不同clusters的數(shù)據(jù)。
但是,上述算法所遇到的問題就是,如果map中間輸出數(shù)據(jù)的key的分布實(shí)現(xiàn)不知道的話,就無法在clusters上使用精確的判別機(jī)制來進(jìn)行切分。而這樣的算法只有在MR執(zhí)行前進(jìn)行才會(huì)顯得有意義,因此本文提出動(dòng)態(tài)范圍分區(qū)(dynamic range partition)的方法,對(duì)輸入數(shù)據(jù)進(jìn)行以此預(yù)先的抽樣,并將抽樣結(jié)果輸入給一小部分的map tasks中,以實(shí)現(xiàn)分布統(tǒng)計(jì),通過相應(yīng)的統(tǒng)計(jì)值,可以預(yù)測(cè)出map階段后所產(chǎn)生冊(cè)所有clusters的大小,這個(gè)結(jié)果將會(huì)作為split策略的輸入。
- 注意,所有的策略均在真正spark程序運(yùn)行之前進(jìn)行 *
綜上所述,本文所做的工作就是在原有spark架構(gòu)基礎(chǔ)上添加了一個(gè)統(tǒng)計(jì)分布功能用于抽樣并作為split和combine算法的輸入。這是一種細(xì)粒度(fine-grained)的算法,他能夠重新進(jìn)行partition,改變?cè)蟹謪^(qū)的大小并發(fā)送給相應(yīng)的buckets,從而解決了reduce端數(shù)據(jù)傾斜問題。
4 主要貢獻(xiàn)
使用水塘抽樣進(jìn)行輸入數(shù)據(jù)的采樣,并提出一種驗(yàn)證模型來選擇合適的采樣率。這樣的模型在運(yùn)行中考慮到了成本、效果以及方差的重要性。
提出了一種切分以及合并鍵值對(duì)clusters的算法。通過以相同大小的clusters的組合來填充固定數(shù)量的buckets從而達(dá)到reduce端具有更好的負(fù)載均衡。
文章基于spark1.1.0驗(yàn)證了本文算法模型帶來的性能提升和數(shù)據(jù)傾斜問題的減緩。
5 系統(tǒng)整體介紹
由前面的術(shù)語介紹以及mapreduce的原理可知:每一個(gè)split會(huì)由一個(gè)map來處理,并生成一中間結(jié)果,中間結(jié)果通常是被partitioned的,而且被分到相應(yīng)的bucket中去。本文使用如下表達(dá)式來來自 m map Tasks的中間結(jié)果:
根據(jù)bucket的概念,我們可以用下面的公式來表達(dá)一個(gè)cluster所分成的buckets:
所有屬于相同cluster的鍵值集合被放置在同一個(gè)partition中,所以根據(jù)key可以將所有的K進(jìn)行n個(gè)partition的劃分,用如下表示:
下圖流程是一個(gè)被改善過的Spark工作流程。
其中最重的組件就是 load balancing module。spark運(yùn)行之前,這個(gè)組件會(huì)生成一個(gè)balanced partitioning策略,這個(gè)策略會(huì)指導(dǎo)該如何split clusters以及如何combine分割后的clusters。balance策略能夠被使用在shuffle階段。
關(guān)于load balancing module,在本文共包含以下兩個(gè)階段:
- Data samling:抽樣是在map之前進(jìn)行的,所謂的抽樣就是從全部的輸入split采樣小樣本,進(jìn)而通過對(duì)key的統(tǒng)計(jì)方法來預(yù)測(cè)map輸出結(jié)果中每一個(gè)cluster的大小。
- Splitting and combination:抽樣結(jié)束后,會(huì)產(chǎn)生一個(gè)clusters的切分和combine方法。一些過大的clusters可能會(huì)被切分多個(gè)以填充到不同的buckets中去。切分的主要還是要看固定的bucket的容量大小。
6 抽樣模型
6.1 數(shù)據(jù)偏移模型
模型的本質(zhì)就是對(duì)clusters、bucket等概念進(jìn)行結(jié)構(gòu)化,通過對(duì)每一個(gè)組件進(jìn)行結(jié)構(gòu)化的分析,最終引出偏移與均值和方差的條件關(guān)系,推導(dǎo)出FoS這樣一個(gè)偏移程度的指標(biāo)。因此,文中定義以下幾個(gè)公式:
1)因?yàn)橐粋€(gè)cluster中是包含相同key的鍵值對(duì)數(shù)據(jù),因此,所有的clusters能夠被形式化如下:
C={C1,C2....Ci...,Cm}, 1<=i<=m
m是cluster的編號(hào),Ci被定義為一種結(jié)構(gòu)體:Ci ={order,SC}
,其中order記錄了這個(gè)cluster的順序,而SC被定義為一個(gè)序列:SC = {SC1,SC2....,SCi...SCm} 1<=i<=m
.其中SCi是整形變量,表示對(duì)應(yīng)的cluster的數(shù)據(jù)集的大小。
2)bucket可以形式化為:B = {B1,B2,....Bk,...Bn} 1<=k<=n
其中n為bucket的編號(hào)
3)前面說到,文本數(shù)據(jù)的key一般服從Zipf分布,這個(gè)模型里面設(shè)置了一個(gè)變量a(0.1<=a<=1.2)來控制偏移量,a越大代表偏移越大,a僅僅影響當(dāng)前輸入數(shù)據(jù)的偏移大小。本文使用矩陣P來表示這樣一種分布,pki表示從cluster Ci所獲得的鍵值對(duì)的數(shù)量,這個(gè)數(shù)量后面將會(huì)被bucket Bk所拉取(說到這,大家還是要搞清楚文中這幾個(gè)術(shù)語之間的關(guān)系的:map的輸出數(shù)據(jù)相當(dāng)于一堆鍵值對(duì),擁有相同key的數(shù)據(jù)<tuples>組成一個(gè)cluster,而同時(shí)被相同reduce處理的clusters組成一個(gè)partition,partition最后會(huì)被放入bucket中),因此SCi可以被看作是在所有buckets中來自cluster Ci的總大小,可以用如下公式來定義:
4)bucket k中所包含的鍵值對(duì)的個(gè)數(shù)由BC(k)
來表示,對(duì)于數(shù)據(jù)的分布pki,外加上之前給的變量a,則分布可以表示為:
那么BC(k)的值可以表示為如下公式:
5)由上式,我們可以計(jì)算在當(dāng)前偏移參數(shù)a的前提下的所有buckets的鍵值對(duì)的平均數(shù)量如下公式所示,其中,n代表的是全部bucket的數(shù)量。
6)一般而言,被bucket所處理的中間數(shù)據(jù)能夠被看作是具有數(shù)據(jù)偏移標(biāo)準(zhǔn)差的,那么,當(dāng)滿足以下條件時(shí),被bucket所處理的clusters將被視作具有數(shù)據(jù)偏移,std是所有buckets中key-value對(duì)個(gè)數(shù)的標(biāo)準(zhǔn)差:
7)所有clusters中的數(shù)據(jù)的標(biāo)準(zhǔn)差可以用以下公示表達(dá):
8)最后,為了歸一化表達(dá)數(shù)據(jù)偏移的程度,定義了FoS指標(biāo)(factor of skew)來評(píng)測(cè)所有buckets負(fù)載均衡的程度,FoS指標(biāo)值越小,則代表負(fù)載越均衡,則偏移也相應(yīng)的越小:
6.2 水塘抽樣算法
為何要使用水塘抽樣?
在一般的編程語言中,常規(guī)的抽樣是使用偽隨機(jī)數(shù),對(duì)于大規(guī)模的數(shù)據(jù),特別是隨著采樣空間的增加,這樣簡(jiǎn)單的偽隨機(jī)數(shù)不能保證所有樣本完全隨機(jī)化,不可避免地會(huì)產(chǎn)生一些重復(fù)樣本。而水塘抽樣則能夠有效避免這一問題,他將使得樣本出現(xiàn)的概率均相同,保證樣本的隨機(jī)性,特別是從某些序列流數(shù)據(jù)中抽取數(shù)據(jù)時(shí),水塘抽樣可以保證原始key的分布更加接近于整體真實(shí)情況。
水塘抽樣是為了解決未知大小數(shù)據(jù)集的隨機(jī)數(shù)抽取問題,要求從一個(gè)未知大小的數(shù)據(jù)集中等概率地拿出k個(gè)數(shù)。尤其是在大數(shù)據(jù)背景下的采樣問題,對(duì)于大規(guī)模數(shù)據(jù),我們無法將其全部加載到內(nèi)存,此時(shí)需要根據(jù)內(nèi)存大小k來等概率地從全部數(shù)據(jù)中抽取大小為k的數(shù)。
水塘抽樣基本思想:
1、簡(jiǎn)單場(chǎng)景:如果我們已知這個(gè)數(shù)據(jù)集只有3個(gè)數(shù)字,那么我們?cè)谀萌〉谝粋€(gè)數(shù)的時(shí)候,其出現(xiàn)在水池中的概率為1,拿取第二個(gè)數(shù)的時(shí)候,其出現(xiàn)在水池中的概率為1/2,在拿取最后一個(gè)數(shù)的時(shí)候,我們?yōu)榱说雀怕实胤祷?個(gè)數(shù),分為兩種情況:1)返回第三個(gè)數(shù):顯然如果要保留第三個(gè)數(shù)則齊概率為1/3。2)如果返回前兩個(gè)數(shù)的其中一個(gè),則其概率為(1-1/3)1/2=1/3,即不選擇第三個(gè)數(shù)的概率選擇前兩個(gè)數(shù)任意一個(gè)的概率,因此水塘中每個(gè)數(shù)字出現(xiàn)的概率均相同。
2、復(fù)雜場(chǎng)景:文中的抽樣方法是需要返回k個(gè)數(shù),因此這里直接修改1中的返回一個(gè)數(shù)為k個(gè)數(shù)即可,即:每個(gè)數(shù)字在水池中出現(xiàn)的概率為k/n。
其算法流程如下:
1)初始化時(shí),我們依次將前k個(gè)數(shù)加載到水池中。
2)隨后考慮第k+1個(gè)數(shù)的生死。此時(shí)分兩種情況:
a.水池中全部元素沒有被替換
b.水池中某個(gè)元素被第k+1個(gè)替換
先來看情況b:對(duì)于第k個(gè)元素,此時(shí)生成一個(gè)0i(i=k+1)的隨機(jī)數(shù)p,如果p<k(相當(dāng)于生成01的隨機(jī)數(shù)),則第k+1個(gè)數(shù)被選中,并且用這個(gè)數(shù)去替換水池中的某一個(gè)數(shù),此時(shí)第k+1個(gè)數(shù)在水庫中出現(xiàn)的概率為k/k+1,接下來我們要看看水庫中每個(gè)元素被替換的概率:條件概率,首先要第k+1個(gè)數(shù)被選中,其次是k個(gè)數(shù)中隨機(jī)選出一個(gè)來替換,則k個(gè)數(shù)中被替換的概率為(k/k+1)*(1/k)=1/k+1.那么水庫中原先的k個(gè)數(shù)每個(gè)數(shù)還能繼續(xù)出現(xiàn)的概率就等于1-1/(k+1)=k/k+1,可以看出,不管是新來的元素還是以前的元素的出現(xiàn)概率均為k/k+1。
對(duì)于情況a:如果所有元素都沒有被替換,就說明第k+1個(gè)元素沒有被選中,則此時(shí)水池中每個(gè)元素出現(xiàn)的概率就為1-第k+1個(gè)元素被選中的概率=1-k/(k+1)=1/k+1
這樣的一個(gè)規(guī)律可以用數(shù)學(xué)歸納法,直到證明完第i+1個(gè)數(shù)時(shí)仍然成立即可。
下面是水塘抽樣算法的偽代碼部分://stream代表數(shù)據(jù)流 //reservoir代表返回長度為k的池塘 //從stream中取前k個(gè)放入reservoir; for ( int i = 1; i < k; i++) reservoir[i] = stream[i]; for (i = k; stream != null; i++) { p = random(0, i); if (p < k) reservoir[p] = stream[i]; return reservoir;
6.3 cluster容量大小的預(yù)測(cè)
建立在抽樣算法的輸出結(jié)果上,同時(shí),基于抽樣算法中的假設(shè):抽樣得到的數(shù)據(jù)的key的分布與整體數(shù)據(jù)的key的分布是相同的,因此,能夠大致預(yù)測(cè)出每個(gè)map節(jié)點(diǎn)上每個(gè)cluster的數(shù)據(jù)大小。
步驟:
- 輸入數(shù)據(jù)是抽樣算法的輸出結(jié)果,用BS表示;同時(shí)MRjob用來表示處理BS的spark作業(yè)。
- 基于部署在map節(jié)點(diǎn)上的監(jiān)控工具,可以得到clusters個(gè)數(shù)的記錄,同時(shí)獲得每個(gè)cluster中k/v的個(gè)數(shù){SCi}.
- 由于抽樣數(shù)據(jù)中的每一個(gè)key的數(shù)量被認(rèn)為是與原始數(shù)據(jù)中key的數(shù)量成比例的,因此可以通過擴(kuò)大每一個(gè)clusters的個(gè)數(shù)來來近似估計(jì)cluster的真實(shí)大小。
7 Cluster的切分和重組模型(5. Cluster splitting and combining)
-
算法流程概述:
clusters的切分和重組算法實(shí)際就是想法設(shè)法將全部的clusters打包并分配到大小相近的buckets中去。整體流程可參考下圖:
算法的輸入是前面我們所定義的{Ci}:clusters tuples的集合;{SCi}:每一個(gè)cluster在{Ci}中集合的個(gè)數(shù);B:當(dāng)前buckets的序列;RB:當(dāng)前bucket剩余容量。算法的輸出是矩陣P,代表著clusters的存放策略。
整體思想是先對(duì)map的中間結(jié)果產(chǎn)生的clusters進(jìn)行降序排序,然后從最大的cluster(SCm)開始判斷其與固定bucket(RB)的大小關(guān)系,如果SCm>=RB,則對(duì)SCm進(jìn)行切分,只將滿足bucke大小的那一部分放進(jìn)第一個(gè)bucket中,剩余的一部分作為一個(gè)segment將進(jìn)入第二輪判斷(注意,被spilt的剩余部分在第二輪迭代前會(huì)與其他的clusters進(jìn)行重新排序),以此類推;如果SCm<=RB,則直接將其放入該bucket中然后判斷下一個(gè)SCm-1是否滿足剩余空間大小,以此類推。整個(gè)流程分輪次進(jìn)行迭代判斷,也就是說,每一次只會(huì)填充一個(gè)bucket。其中RB是表示當(dāng)前bucket剩余空間大小,隨著clusters的填充,RB的值會(huì)不斷更新,某一輪的停止條件為尋找到一個(gè)SC的大小大于RB,則表示當(dāng)前輪的bucket即將被填滿。
那么問題來了:
在spark-1.1.0中,當(dāng)map的中間輸出達(dá)到20%時(shí),就會(huì)有shuffle階段啟動(dòng),那么說明我們是無法等到全部clusters都生成后才去得到切分和重組策略!!!
對(duì)于這個(gè)問題,其實(shí)我們需要知道,切分和重組算法其實(shí)相當(dāng)于一個(gè)模擬的過程,我們?cè)谧鳂I(yè)執(zhí)行前通過采樣得到了一組采樣數(shù)據(jù),這組采樣數(shù)據(jù)能夠真實(shí)反映整體數(shù)據(jù)在map輸出時(shí)的大致分布情況,因此,在進(jìn)行切分和重組算法時(shí),模型只需輸出在已知clusters分布的情況下的一個(gè)模擬bucket分配方式即可。
切分和重組算法的輸出矩陣P如下圖所示:Pij的定義回顧一下:第i個(gè)bucket從第j個(gè)cluster所拉取的k/v的數(shù)量。
這個(gè)矩陣可以換一種形式來表示,下面的形式將直接表示出每個(gè)cluster中被每個(gè)bucket所處理的k/v的數(shù)量。
2.真實(shí)作業(yè)時(shí)的Cluster分配算法
真實(shí)作業(yè)時(shí),map達(dá)到20%輸出時(shí)就會(huì)進(jìn)行shuffle,此算法就是根據(jù)先前的模擬放置策略來動(dòng)態(tài)決定當(dāng)前輸出數(shù)據(jù)的存放。算法以矩陣P以及{Ci}(每一個(gè)clusters的大小)為輸入,以{CBij}來表示每一個(gè)bucket的當(dāng)前負(fù)載情況,即代表當(dāng)前第i個(gè)bucket所拉取的第j個(gè)cluster的數(shù)據(jù)大小。
算法流程:
該算法用于真實(shí)作業(yè)情況下中間數(shù)據(jù)的放置。有兩種基本情況
1)首先對(duì)C進(jìn)行遍歷獲得Ki的位置,如果此時(shí)在C中找不到Ki,則按照默認(rèn)的hash來放置Ki。
2)如果找得到,則代表可以在矩陣P中獲取到該Ki。算法中實(shí)現(xiàn)這一過程其實(shí)是去遍歷相關(guān)列向量pj,在矩陣P中,我們知道列序號(hào)代表著key在clusters集合C中的位置,因此,對(duì)于向量pj,每一個(gè)元素都表示著該key在每個(gè)bucket中所被允許的最大的容量。解釋完pj,則這個(gè)流程可以概括為:在遍歷pj的同時(shí),來判斷當(dāng)前每一個(gè)bucket的負(fù)載情況BCij是否小于pij的極限,也就是為了判斷Ki的位置,算法將會(huì)遍歷所有的bucket。
8 實(shí)驗(yàn)環(huán)境
1、16節(jié)點(diǎn)
2、spark-1.1.0 backend:HDFS
3、用例:
1)sort,該用例的數(shù)據(jù)應(yīng)該是使用的谷歌全球排序比賽的數(shù)據(jù)
排序數(shù)據(jù)分為兩種:Daytona (stock car) 和 Indy (formula 1) 。
值得一提的是目前最快紀(jì)錄是騰訊。
2)Text Search 數(shù)據(jù):(English Wikipedia
archive data set)[https://en.wikipedia.org/wiki/Archive]
3)WordCount,該用例的數(shù)據(jù)未在文中看到...........
我的博客 : https://NingSM.github.io
轉(zhuǎn)載請(qǐng)注明原址,謝謝。