起因
近日在工作中遇到一個Hive job報錯,查看報錯信息如下:
問題猜測是由于MapJoin導致了oom,經指點后設置如下參數
set mapred.max.split.size=96000000;
set mapred.min.split.size.per.node=96000000;
set mapred.min.split.size.per.rack=96000000;
Job 成功執行。具體原理經查閱資料后,整理如下:
原理
輸入分片(Input Split)
在進行map計算前,MapReduce會根據輸入文件計算輸入分片(Input Split),每個輸入分片針對一個map任務,輸入分片存儲的并非數據本身,而是一個分片長度和一個記錄數據位置的數組
在Hadoop2.x中默認的block大小是128M,在1.x中默認的大小是64M,可是在hdfs-site.xml中自行設置:
dfs.block.size,單位是byte
分片的大小范圍可以在mapred-site.xml中設置
mapred.min.split.size,默認為1B
mapred.max.split.size,默認為Long.MAX_VALUE=9223372036854775807
分片大小
minSize=max(minSplitSize,mapred.min.split.size)
maxSize=mapred.max.split.size
splitSize=max(minSize,min(maxSize,blockSize))
所以,當沒有設置分片范圍的時候,block塊的大小決定了分片的大小,比如把一個258M的文件上傳至HDFS,假設block的大小是128M,那么它就會被分成3個block塊,與之相對應產生三個split,最終會有三個map task。這樣產生一個新的問題,繼續上面的例子,第3個block塊存的文件大小只有2M,而它的block塊的大小是128M,那它實際占用的Linux file system的空間是多大?答案是文件的實際大小;那在這種情況下,block大小的意義在于,當文件通過append操作不斷增長時,可以通過block的大小決定何時split 文件。
計算map個數
blockSize:HDFS的文件塊大小
totalSize:輸入文件大小
inputFileNum:輸入文件的數量
- 默認map個數
如果不進行任何設置,默認的map個數是和blockSize相關的,defaultNum=totalSize/blockSize - 期望個數
可以通過參數mapred.map.tasks來設置期望的map個數,但這個只有在大于默認map個數的時候才生效,goalNum=mapred.map.tasks - 設置處理的文件大小
可以通過mapred.min.split.size設置每個task處理的文件大小,但是這個大小只有在大于blockSize的時候才會生效
splitSize=max(mapred.min.split.size,blockSize)
splitNum=totalSize/splitSize - 計算的map個數
computeMapNum=min(splitNum,max(defaultNum,goalNum))
除了以上這些配置外,MapReduce還要遵循一些原則。MapReduce的每一個map處理數據是不能跨越文件的。也就是說minMapNum>=inputFileNum,所以,最終的map個數應該為:
mapNum=max(computeMapNum,inputFileNum)