轉自:http://www.cnblogs.com/joyeecheung/p/3841952.html
相關隨筆:
- Hadoop-1.0.4集羣搭建筆記
- 用python + hadoop streaming 編寫分佈式程序(一) -- 原理介紹,樣例程序與本地調試
- 用python + hadoop streaming 編寫分佈式程序(二) -- 在集羣上運行與監控
使用額外的文件
假如你跑的job除了輸入以外還需要一些額外的文件(side data),有兩種選擇:
-
大文件
所謂的大文件就是大小大於設置的local.cache.size的文件,默認是10GB。這個時候可以用-file來分發。除此之外代碼本身也可以用file來分發。
格式:假如我要加多一個sideData.txt給python腳本用:
$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ -input iputDir \ -output outputDir \ -mapper mapper.py \ -file mapper.py \ -reducer reduer.py \ -file reducer.py \ -file sideDate.txt
在python腳本里,只要把這個文件當成自己同一目錄下的本地文件來打開就可以了。比如:
f = open("sideData.txt")
注意這個file是隻讀的,不可以寫。
-
小文件
如果是比較小的文件,想要提高讀寫速度可以將它放在distributed cache裏(也就是每臺機器都有自己的一份copy,不需要網絡IO就可以拿到數據)。這裏要用到的參數是-cachefile,寫法和用法上一個一樣,就是-file改成-cachefile而已。
控制partitioner
partitioning指的是數據經過mapper處理後,被分發到reducer上的過程。partitioner控制的,就是“怎樣的mapper輸出會被分發到哪一個reducer上”。
Hadoop有幾個自帶的partitioner,解釋可以看這裏。默認的是HashPartitioner,也就是把第一個tab前的key做hash之後用於分配partition。寫Hadoop Streaming程序是可以選擇其他partitioner的,你可以選擇自帶的其他幾種裏的一種,也可以自己寫一個繼承Partitioner的java類然後編譯成jar,在運行參數裏指定爲你用的partitioner。
官方自帶的partitioner裏最常用的是KeyFieldBasedPartitioner(源代碼可以看這裏)。它會按照key的一部分來做partition,而不是用整個key來做partition。
在學會用KeyFieldBasedPartitioner之前,必然要先學怎麼控制key-value的分割。分割key的步驟可以分爲兩步,用python來描述一下大約是
fields = output.split(seperator)
key = fields[:numKeyfields]
-
選擇用什麼符號來分割key,也就是選擇seperator
map.output.key.field.separator可以指定用於分隔key的符號。比如指定爲一點的話,就要加上參數
-D stream.map.output.field.separator=.
假設你的mapper輸出是
11.22.33.44
這時會先看準[11, 22, 33, 44]這裏的其中一個或幾個作爲key
-
選擇key的範圍,也就是選擇numKeyfields
控制key的範圍的參數是這個,假設我要設置被分割出的前2個元素爲key:
-D stream.num.map.output.key.fields=2
那麼key就是上面的 1122。值得注意的是假如這個數字設置到覆蓋整個輸出,在這個例子裏是4的話,那麼整一行都會變成key。
上面分割出key之後, KeyFieldBasedPartitioner還需要知道你想要用key裏的哪部分作爲partition的依據。它進行配置的過程可以看源代碼來理解。
假設在上一步我們通過使用
-D stream.map.output.field.separator=. \
-D stream.num.map.output.key.fields=4 \
將11.22.33.44的整個字符串都設置成了key,下一步就是在這個key的內部再進行一次分割。map.output.key.field.separator可以用來設置第二次分割用的分割符,mapred.text.key.partitioner.options可以接受參數來劃分被分割出來的partition key,比如:
-D map.output.key.field.separator=. \
-D mapred.text.key.partitioner.options=-k1,2 \
指的就是在key的內部裏,將第1到第2個被點分割的元素作爲partition key,這個例子裏也就是1122。這裏的值-ki,j表示從i到j個元素(inclusive)會作爲partition key。如果終點省略不寫,像-ki的話,那麼i和i之後的元素都會作爲partition key。
partition key相同的輸出會保證分到同一個reducer上,也就是所有11.22.xx.xx的輸出都會到同一個partitioner,11.22換成其他各種組合也是一樣。
實例說明一下,就是這樣的:
-
mapper的輸出是
11.12.1.2 11.14.2.3 11.11.4.1 11.12.1.1 11.14.2.2
-
指定前4個元素做key,key裏的前兩個元素做partition key,分成3個partition的話,就會被分成
11.11.4.1 ----------- 11.12.1.2 11.12.1.1 ----------- 11.14.2.3
11.14.2.2 -
下一步reducer會對自己得到的每個partition內進行排序,結果就是
11.11.4.1 ----------- 11.12.1.1 11.12.1.2 ----------- 11.14.2.2 11.14.2.3
命令格式大約就是長這樣
$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \
-D stream.map.output.field.separator=. \
-D stream.num.map.output.key.fields=4 \
-D map.output.key.field.separator=. \
-D mapred.text.key.partitioner.options=-k1,2 \
-input inputDir \
-output outputDir \
-mapper mapper.py -file mapper.py \
-reducer reducer.py -file reducer.py \
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner
注意-D參數放在前面,指定用KeyFieldBasedPartitioner的-partitioner要放在下面。
控制comparator與自定義排序
上面說到mapper的輸出被partition到各個reducer之後,會有一步排序。這個排序的標準也是可以通過設置comparator控制的。和上面一樣,要先設置分割出key用的分隔符、key的範圍,key內部分割用的分隔符
-D stream.map.output.field.separator=. \
-D stream.num.map.output.key.fields=4 \
-D map.output.key.field.separator=. \
這裏要控制的就是key內部的哪些元素用來做排序依據,是排字典序還是數字序,倒序還是正序。用來控制的參數是mapred.text.key.comparator.options,接受的值格式類似於unix sort。比如我要按第二個元素的數字序(默認字典序)+倒序來排元素的話,就用
-D mapred.text.key.comparator.options=-k2,2nr
n表示數字序,r表示倒序。這樣一來
11.12.1.2
11.14.2.3
11.11.4.1
11.12.1.1
11.14.2.2
就會被排成
11.14.2.3
11.14.2.2
11.12.1.2
11.12.1.1
11.11.4.1
eg:
hadoop jar /usr/lib/hadoop-0.20-mapreduce/contrib/streaming/hadoop-streaming.jar \
-input $output \
-output $output2 \
-mapper "cat" \
-reducer "awk 'BEGIN{FS=\"\t\";OFS=\",\";tp=0;tp2=0;n=0}{split(\$1,d,\",\");if(tp==0){tp=d[1];tp2=d[2];n=1}else if(tp!=d[1]){print tp,n;tp=d[1];tp2=d[2];n=1}else if(tp2!=d[2]){n++;tp2=d[2]}}END{print tp,n}'" \
-jobconf stream.map.output.field.separator="," \
-jobconf stream.num.map.output.key.fields=2 \
-jobconf map.output.key.field.separator="," \
-jobconf num.key.fields.for.partition=1 \
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \
-jobconf mapred.reduce.tasks=10 \
-jobconf mapred.job.name="hh" \