Hadoop排序工具用法小結

Hadoop用於對key的排序和分桶的設置選項比較多和複雜,目前在公司內主要以KeyFieldBasePartitioner和KeyFieldBaseComparator被hadoop用戶廣泛使用。

基本概念

Partition:分桶過程,用戶輸出的key經過partition分發到不同的reduce裏,因而partitioner就是分桶器,一般用平臺默認的hash分桶也可以自己指定。
Key:是需要排序的字段,相同分桶&&相同key的行排序到一起。

下面以一個簡單的文本作爲例子,通過搭配不同的參數跑出真實作業的結果來演示這些參數的使用方法。
假設map的輸出是這樣以點號分隔的若干行:

d.1.5.23
e.9.4.5
e.5.9.22
e.5.1.45
e.5.1.23
a.7.2.6
f.8.3.3

我們知道,在streaming模式默認hadoop會把map輸出的一行中遇到的第一個設定的字段分隔符前面的部分作爲key,後面的作爲value,如果輸出的一行中沒有指定的字段分隔符,則整行作爲key,value被設置爲空字符串。 那麼對於上面的輸出,如果想用map輸出的前2個字段作爲key,後面字段作爲value,並且不使用hadoop默認的“\t”字段分隔符,而是根據該文本特點使用“.”來分割,需要如何設置呢

bin/hadoop streaming -input /tmp/comp-test.txt -output /tmp/xx -mapper cat -reducer cat \
-jobconf stream.num.map.output.key.fields=2 \
-jobconf stream.map.output.field.separator=. \
-jobconf mapred.reduce.tasks=5

結果:

e.9 4.5
f.8 3.3
——————
d.1 5.23
e.5 1.23
e.5 1.45
e.5 9.22
——————
a.7 2.6

總結:

從結果可以看出,在reduce的輸出中,前兩列和後兩列用“\t”分隔,證明map輸出時確實把用“.”分隔的前兩列作爲key,後面的作爲value。並且前兩列相同的“e.5”開頭的三行被分到了同一個reduce中,證明確實以前兩列作爲key整體做的partition。
stream.num.map.output.key.fields 設置map輸出的前幾個字段作爲key
stream.map.output.field.separator 設置map輸出的字段分隔符

KeyFieldBasePartitioner的用法

如果想要靈活設置key中用於partition的字段,而不是把整個key都用來做partition。就需要使用hadoop中的org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner了。
下面只用第一列作partition,但依然使用前兩列作爲key。

bin/hadoop streaming -input /tmp/comp-test.txt -output /tmp/xx -mapper cat -reducer cat \
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \
-jobconf stream.num.map.output.key.fields=2 \
-jobconf stream.map.output.field.separator=. \
-jobconf map.output.key.field.separator=. \
-jobconf num.key.fields.for.partition=1 \
-jobconf mapred.reduce.tasks=5

結果:

d.1 5.23
——————
e.5 1.23
e.5 1.45
e.5 9.22
e.9 4.5
——————
a.7 2.6
f.8 3.3

總結:

從結果可以看出,這次“e”開頭的行都被分到了一個桶內,證明做partition是以第一列爲準的,而key依然是前兩列。並且在同一個partition內,先按照第一列排序,第一列相同的,按照第二列排序。這裏要注意的是使用map.output.key.field.separator來指定key內字段的分隔符,這個參數是KeyFieldBasePartitioner和KeyFieldBaseComparator所特有的。
map.output.key.field.separator 設置key內的字段分隔符
num.key.fields.for.partition 設置key內前幾個字段用來做partition

事實上KeyFieldBasePartitioner還有一個高級參數mapred.text.key.partitioner.options,這個參數可以認爲是num.key.fields.for.partition的升級版,它可以指定不僅限於key中的前幾個字段用做partition,而是可以單獨指定key中某個字段或者某幾個字段一起做partition。
比如上面的需求用mapred.text.key.partitioner.options表示爲
mapred.text.key.partitioner.options=-k1,1
注意mapred.text.key.partitioner.optionsnum.key.fields.for.partition不需要一起使用,一起使用則以num.key.fields.for.partition爲準。

這裏再舉一個例子,使用mapred.text.key.partitioner.options

bin/hadoop streaming -input /tmp/comp-test.txt -output /tmp/xx -mapper cat -reducer cat \
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \
-jobconf stream.num.map.output.key.fields=3 \
-jobconf stream.map.output.field.separator=. \
-jobconf map.output.key.field.separator=. \
-jobconf mapred.text.key.partitioner.options=-k2,3 \
-jobconf mapred.reduce.tasks=5

結果:

e.9.4   5
——————
a.7.2   6
e.5.9   22
——————
d.1.5   23
e.5.1   23
e.5.1   45
f.8.3   3

可見,這次是以前3列作爲key的,而partition則以key中的第2-3列,因此以“e”開頭的行被拆散了,但第二三列相同的“5,1”被分到一個桶內。在同一個桶內,依然是從key的第一列開始排序的,注意,KeyFieldBasePartitioner隻影響分桶並不影響排序。
mapred.text.key.partitioner.options 設置key內某個字段或者某個字段範圍用做partition

KeyFieldBaseComparator的用法

首先簡單解釋一下hadoop框架中key的comparator,對於hadoop所識別的所有java的key類型(在框架看來key的類型只能是java的),很多類型都自定義了基於字節的比較器,比如Text,IntWritable等等,如果不特別指定比較器而使用這些類型默認的,則會將key作爲一個整體的字節數組來進行比較。而KeyFieldBaseComparator則相當於是一個可以靈活設置比較位置的高級比較器,但是它並沒有自己獨有的比較邏輯,而是使用默認Text的基於字典序或者通過-n來基於數字比較。
之前的例子使用KeyFieldBasePartitioner自定義了使用key中的部分字段做partition,現在我們通過org.apache.hadoop.mapred.lib.KeyFieldBasedComparator來自定義使用key中的部分字段做比較。

這次把前四列都作爲key,前兩列做partition,排序依據優先依據第三列正序(文本序),第四列逆序(數字序)的組合排序。

bin/hadoop streaming -input /tmpcomp-test.txt -output /tmp/xx -mapper cat -reducer cat \
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \
-jobconf mapred.output.key.comparator.class=org.apache.hadoop.mapred.lib.KeyFieldBasedComparator \
-jobconf stream.num.map.output.key.fields=4 \
-jobconf stream.map.output.field.separator=. \
-jobconf map.output.key.field.separator=. \
-jobconf mapred.text.key.partitioner.options=-k1,2 \
-jobconf mapred.text.key.comparator.options="-k3,3 -k4nr" \
-jobconf mapred.reduce.tasks=5

結果:

e.5.1.45
e.5.1.23
d.1.5.23
e.5.9.22
——————
a.7.2.6
——————
f.8.3.3
e.9.4.5

總結:

從結果可以看出,符合預期的按照先第三列文本正序,然後第四列基於數字逆序的排序。
另外注意,如果這種寫法
mapred.text.key.comparator.options="-k2"
則會從第二列開始,用字典序一直比較到key的最後一個字節。所以對於希望準確排序字段的需求,還是使用“k2,2”這種確定首尾範圍的形式更好。另外如果給定的key中某些行需要排序的列數不夠時,會比較到最後一列,缺列的行默認缺少的那一列排序值最小。
mapred.text.key.comparator.options 設置key中需要比較的字段或字節範圍

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章