1. 程式人生 > >MapReduce Input Split(輸入分/切片)詳解

MapReduce Input Split(輸入分/切片)詳解

看了很多部落格,感覺沒有一個說的很清楚,所以我來整理一下。

先看一下這個圖

輸入分片(Input Split):在進行map計算之前,mapreduce會根據輸入檔案計算輸入分片(input split),每個輸入分片(input split)針對一個map任務,輸入分片(input split)儲存的並非資料本身,而是一個分片長度和一個記錄資料的位置的陣列。

Hadoop 2.x預設的block大小是128MB,Hadoop 1.x預設的block大小是64MB,可以在hdfs-site.xml中設定dfs.block.size,注意單位是byte。

分片大小範圍可以在mapred-site.xml中設定,mapred.min.split.size mapred.max.split.size,

minSplitSize大小預設為1B,maxSplitSize大小預設為Long.MAX_VALUE = 9223372036854775807

那麼分片到底是多大呢?

minSize=max{minSplitSize,mapred.min.split.size} 

maxSize=mapred.max.split.size

splitSize=max{minSize,min{maxSize,blockSize}}

我們再來看一下原始碼


所以在我們沒有設定分片的範圍的時候,分片大小是由block塊大小決定的,和它的大小一樣。比如把一個258MB的檔案上傳到HDFS上,假設block塊大小是128MB,那麼它就會被分成三個block
塊,與之對應產生三個split
,所以最終會產生三個map task。我又發現了另一個問題,第三個block塊裡存的檔案大小隻有2MB,而它的block塊大小是128MB,那它實際佔用Linux file system的多大空間?

答案是實際的檔案大小,而非一個塊的大小。

有大神已經驗證這個答案了:http://blog.csdn.net/samhacker/article/details/23089157

1、往hdfs裡面新增新檔案前,hadoop在linux上面所佔的空間為 464 MB:


2、往hdfs裡面新增大小為2673375 byte(大概2.5 MB)的檔案:

2673375 derby.jar


3、此時,hadoop在linux上面所佔的空間為 467 MB——增加了一個實際檔案大小(2.5 MB)的空間,而非一個block size(128 MB)

4、使用hadoop dfs -stat檢視檔案資訊: 


這裡就很清楚地反映出: 檔案的實際大小(file size)是2673375 byte, 但它的block size是128 MB。

5、通過NameNode的web console來檢視檔案資訊: 


結果是一樣的: 檔案的實際大小(file size)是2673375 byte, 但它的block size是128 MB。

6、不過使用‘hadoop fsck’檢視檔案資訊,看出了一些不一樣的內容——  ‘1(avg.block size 2673375 B)’: 

值得注意的是,結果中有一個 ‘1(avg.block size 2673375 B)’的字樣。這裡的 'block size' 並不是指平常說的檔案塊大小(Block Size)—— 後者是一個元資料的概念,相反它反映的是檔案的實際大小(file size)。以下是Hadoop Community的專家給我的回覆: 

“The fsck is showing you an "average blocksize", not the block size metadata attribute of the file like stat shows. In this specific case, the average is just the length of your file, which is lesser than one whole block.”


最後一個問題是: 如果hdfs佔用Linux file system的磁碟空間按實際檔案大小算,那麼這個”塊大小“有必要存在嗎?

其實塊大小還是必要的,一個顯而易見的作用就是當檔案通過append操作不斷增長的過程中,可以通過來block size決定何時split檔案。以下是Hadoop Community的專家給我的回覆: 

“The block size is a meta attribute. If you append tothe file later, it still needs to know when to split further - so it keeps that value as a mere metadata it can use to advise itself on write boundaries.” 


補充:我還查到這樣一段話

原文地址:http://blog.csdn.net/lylcore/article/details/9136555

一個split的大小是由goalSize, minSize, blockSize這三個值決定的。computeSplitSize的邏輯是,先從goalSize和blockSize兩個值中選出最小的那個(比如一般不設定map數,這時blockSize為當前檔案的塊size,而goalSize是檔案大小除以使用者設定的map數得到的,如果沒設定的話,預設是1)。

hadooop提供了一個設定map個數的引數mapred.map.tasks,我們可以通過這個引數來控制map的個數。但是通過這種方式設定map的個數,並不是每次都有效的。原因是mapred.map.tasks只是一個hadoop的參考數值,最終map的個數,還取決於其他的因素。      為了方便介紹,先來看幾個名詞: block_size : hdfs的檔案塊大小,預設為64M,可以通過引數dfs.block.size設定 total_size : 輸入檔案整體的大小 input_file_num : 輸入檔案的個數 (1)預設map個數      如果不進行任何設定,預設的map個數是和blcok_size相關的。      default_num = total_size / block_size; (2)期望大小      可以通過引數mapred.map.tasks來設定程式設計師期望的map個數,但是這個個數只有在大於default_num的時候,才會生效。      goal_num = mapred.map.tasks; (3)設定處理的檔案大小      可以通過mapred.min.split.size 設定每個task處理的檔案大小,但是這個大小隻有在大於block_size的時候才會生效。      split_size = max(mapred.min.split.size, block_size);      split_num = total_size / split_size; (4)計算的map個數 compute_map_num = min(split_num,  max(default_num, goal_num))      除了這些配置以外,mapreduce還要遵循一些原則。 mapreduce的每一個map處理的資料是不能跨越檔案的,也就是說min_map_num >= input_file_num。 所以,最終的map個數應該為:      final_map_num = max(compute_map_num, input_file_num)      經過以上的分析,在設定map個數的時候,可以簡單的總結為以下幾點: (1)如果想增加map個數,則設定mapred.map.tasks 為一個較大的值。 (2)如果想減小map個數,則設定mapred.min.split.size 為一個較大的值。 (3)如果輸入中有很多小檔案,依然想減少map個數,則需要將小檔案merger為大檔案,然後使用準則2。