但是hadoop並不完美,在實際運營中,我發現MapReduce Job仍然經常會因爲一些偶發性錯誤而
運行失敗.所以我決定深入探究一下各種不同因素是如何導致job失敗的.
如果一個hadoop job的某個給定task在失敗預定次(默認是4)後,整個job就會失敗.
這可以通過"mapred.map.max.attempts"和"mapred.reduce.max.attempts"屬性來設置.
一個task可能由於各種偶發原因而失敗 - 比如我發現的情況就有磁盤滿,hadoop本身的bug,或者硬件失效(e.g.: 磁盤只讀).
下面是針對job失敗的概率總結的一個大致公式:
P[個別task失敗的最大次數] = P[task失敗] ^ (task總失敗次數)
P[task成功] = 1 - P[個別task失敗的最大次數]
P[job成功] = P[task成功] ^ (task數量)
P[job失敗] = 1 - P[job成功]
P[job失敗] = 1 - (1 - P[task失敗] ^ (task總失敗次數) ) ^ (task數量)
task失敗的最大次數通過mapred.max.max.tracker.failures設置(默認爲4).
我們來分析一個負載爲100000個map task的job:
task數量 最大失敗數 P[task失敗] P[job失敗]
100000 4 0.01 0.00099950017664
100000 4 0.02 0.015872681207036
100000 4 0.03 0.077806338804489
100000 4 0.04 0.22585828487781
100000 4 0.05 0.46473961691604
100000 4 0.06 0.72637819455556
100000 4 0.01 0.00099950017664
100000 3 0.01 0.095162627208542
100000 3 0.005 0.012422200279389
100000 2 0.005 0.91791756653541
如果task失敗概率低於1%的話,job失敗概率幾乎可以不計. 重點就是保證集羣穩定,保持較小失敗概率 .
我們同樣可以看到"mapred.max.tracker.failures"參數的重要性, 如果其取值小於4時,job失敗的概率明顯上升,就算task失敗概率降低到0.5% .
相較mapper而言, reducer運行的時間更長,這意味着其更容易遭受意外事故.也就是說,我們可以肯定reducer的失敗概率比mapper要大很多.但是從另一方面來說,通常reducer task的數量要小於mapper數量,這個又作了一定補償.
下面我們來看看一組基於reducer的失效概率分析:
task數量 最大失敗數 P[task失敗] P[job失敗]
300 1
0.1 0.99999999999998
300 2
0.1 0.95095910592871
300 3
0.1 0.2592929678439
300 4
0.1 0.029555922215749
300 ; 4 0.01 0.00000299999553
300 4
0.05
0.001873249134037
300 4 0.1 0.029555922215749
300 4
0.2 0.38145442906123
300 4
0.3 0.9128299934708
在整個失效模型中還有一個很重要的因子需要考慮,那就是失效節點.通常若出現整個節點失效,那麼在此節點上運行的所有task都會失敗,失效原因可能是因爲磁盤損壞(通常的症狀是出現 磁盤只讀 或 盤符丟失 ), 磁盤寫滿等.一旦出現壞節點,你會發現在此節點被列入黑名單之前(被job列入黑名單的節點不會被job再次分配其任務),會有一大堆 map/reduce task失敗.爲了簡化我們的分析,我假設給定壞節點會導致固定數量的task失敗.另外,我假設給定task只會在給定壞節點上中招一次, 因爲節點會在不久後被列入黑名單.我們用"b-tasks"來標記在壞節點上失敗過的task, 其它task標記爲"n-tasks"."b-task"會在壞節點上遭受一次失敗, 所以後續如果job再出現"最大task失敗數 - 1"次失敗task就會導致job失敗.在我們的集羣中,我曾發現一個壞節點引發3個task失敗, 那麼我就以此爲據, 給出reduce階段失效概率的公式:
b-tasks數量 = 壞節點數量 * 3
P[所有b-task都成功] = (1 - P[task失敗概率] ^ (最大task失敗數 - 1)) ^ (b-tasks數量)
P[所有n-task都成功] = (1 - P[task失敗] ^ (最大task失敗數)) ^ (task數量 - b-task數量)
P[job成功] = P[所有b-task成功] * P[所有n-task成功]
P[job成功] = (1-P[task失敗]^(最大task失敗數 - 1))^(b-task數量) * (1-P[task失敗]^(最大task失敗數))^(最大task數 - b-task數)
P[job失敗] = 1 - P[job成功]
因爲mapper數量通常較多,所以少數壞節點對於以上公式計算的結果並沒有太大的出入.但對reducer而言,其數量較少,所以以上公式計算出
的結果就有比較明顯的變化:
task數量 最大失敗數 P[task失敗] 壞節點數量 P[job失敗]
300 4 0.1 0
0.02955592221574
300 4 0.1 1
0.03217402532872
300 4 0.1 2
0.03478506521768
300 4 0.1 5
0.04257599583811
300 4 0.05 0 0.00187324913403
300 4 0.05
5
0.00364969637240
300 4 0.05 10
0.00542298192335
300 4 0.05 20
0.00896009046141
值得慶幸的是結果並沒有發生戲劇性的變化 - 在5個壞節點的情況下,只導致失敗率提高到了排除壞節點條件下的1.5~2倍.
最後的結論就是, hadoop在task失效概率保持較低的情況下,容錯性還是很好的.基於前面的一些數據分析,我們發現"最大task失敗數 " 最好設置爲4, 當task失敗率達到1%時,你需要開始考慮集羣的穩定性了.當然,你可以通過增大"最大task失敗數"來提高穩定性,但是如果有太多task失敗, 那麼job執行的性能也會降低.所以再強調一次, 重點還是 --- 保持集羣穩定 .