很少說,直接上乾貨!html
hadoop的分塊有兩部分。
node
第一部分就是數據的劃分(即把File劃分紅Block),這個是物理上真真實實的進行了劃分,數據文件上傳到HDFS裏的時候,須要劃分紅一塊一塊,每塊的大小由hadoop-default.xml裏配置選項進行劃分。網絡
<property> <name>dfs.block.size</name> <value>67108864</value> <description>The default block size for new files.</description> </property>
這個就是默認的每一個塊64MB。數據劃分的時候有冗餘,個數是由如下配置指定的。oop
<property> <name>dfs.replication</name> <value>3</value> <description>Default block replication. The actual number of replications can be specified when the file is created. The default is used if replication is not specified in create time. </description> </property>
具體的物理劃分步驟由Namenode決定。spa
第二種劃分是由InputFormat這個接口來定義的,其中有個getSplits方法。這裏有一個新的概念:fileSplit。每一個map處理一個fileSplit,因此有多少個fileSplit就有多少個map(map數並非單純的由用戶設置決定的)。code
咱們來看一下hadoop分配splits的源碼:orm
if ((length != 0) && isSplitable(fs, path)) { long blockSize = file.getBlockSize(); long splitSize = computeSplitSize(goalSize, minSize, blockSize); long bytesRemaining = length; while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) { int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining); splits.add(new FileSplit(path, length-bytesRemaining, splitSize, blkLocations[blkIndex].getHosts())); bytesRemaining -= splitSize; } if (bytesRemaining != 0) { splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining, blkLocations[blkLocations.length-1].getHosts())); } } else if(length!=0) { splits.add(new FileSplit(path,0,length,blkLocations[0].getHosts())); }else{ // Create empty hosts array for zero length files splits.add(new FileSplit(path,0,length,new String[0])); }
從代碼能夠看出,一個塊爲一個splits,即一個map,只要搞清楚一個塊的大小,就能計算出運行時的map數。而一個split的大小是由goalSize, minSize, blockSize這三個值決定的。computeSplitSize的邏輯是,先從goalSize和blockSize兩個值中選出最小的那個(好比通常不設置map數,這時blockSize爲當前文件的塊size,而goalSize是文件大小除以用戶設置的map數獲得的,若是沒設置的話,默認是1),在默認的大多數狀況下,blockSize比較小。而後再取bloceSize和minSize中最大的那個。而minSize若是不經過」mapred.min.split.size」設置的話(」mapred.min.split.size」默認爲0),minSize爲1,這樣得出的一個splits的size就是blockSize,即一個塊一個map,有多少塊就有多少map。xml
上面說的是splitable的狀況,unsplitable能夠根據實際狀況來計算,通常爲一個文件一個map。htm
下面是摘自網上的一個總結:blog
幾個簡單的結論:
一、一個split不會包含零點幾或者幾點幾個Block,必定是包含大於等於1個整數個Block。
二、 一個split不會包含兩個File的Block,不會跨越File邊界。
三、split和Block的關係是一對多的關係。
四、maptasks的個數最終決定於splits的長度。
還有一點須要說明,在FileSplit類中,有一項是private String[] hosts;
看上去是說明這個FileSplit是放在哪些機器上的,實際上hosts裏只是存儲了一個Block的冗餘機器列表。
好比有個fileSplit 有4個block: Block11, Block12, Block13,Block14,這個FileSplit中的hosts裏最終存儲的是Block11自己和其備份所在的機器列表,也就是說 Block12,Block13,Block14存在哪些機器上沒有在FileSplit中記錄。
FileSplit中的這個屬性有利於調度做業時候的數據本地性問題。若是一個tasktracker前來索取task,jobtracker就會找個 task給他,找到一個maptask,得先看這個task的輸入的FileSplit裏hosts是否包含tasktracker所在機器,也就是判斷 和該tasktracker同時存在一個機器上的datanode是否擁有FileSplit中某個Block的備份。
但總之,只能牽就一個Block,其餘Block就要從網絡上傳。不過對於默認大多數狀況下的一個block對應一個map,能夠經過修改hosts使map的本地化數更多一些。 在講block的hosts傳給fileSplit時,hosts中的主機地址能夠有多個,表示map能夠從優先從這些hosts中選取(只是優先,但hdfs還極可能根據當時的網絡負載選擇不是hosts中的主機起map task)。
知道這個特性以後,能夠修改傳回給fileSplit的hosts,在列表中只寫block所在的那些hosts,這樣hdfs就會優先將這些map放到這些hosts上去執行,因爲hosts上有該block,就省掉了網絡傳輸數據的時間。
這樣作的話,在job不少的時候,可能會出現hot spot,即數據用的越多,它所在hosts上的map task就會越多。因此在考慮修改傳給fileSplit的時候要考慮平衡諸多因素。