從一個小的例子開始:
詳細的MapReduce的工作模式我這里不細講了,可以參考我前面的一篇博客:(http://www.reibang.com/p/ed8141511b8b)這里提下shuffle和sort.
Map和reduce之間的shuffle(洗牌)是將key值相同的放在一塊,sort(排序)是按照key值進行排序.例如like出現(xiàn)了兩次,就會把like放在一起.you也是.然后根據(jù)key值進行按照字典的順序進行排序.下面我想將下面的數(shù)據(jù)按照時間進行排序,并且ID相同的在一塊.具體的數(shù)據(jù)格式:(數(shù)據(jù)簡化成如下,其實還包含其他的一些數(shù)據(jù))
6395 1473840570
6393 1473840390
6393 1473840150
6393 1473840450
6395 1473840030
6395 1473840991
6394 1473839970
6394 1473840811
6394 1473840090
......................
第一列是ID號,第二列是Linux時間戳.想輸出的結(jié)果是:ID號相同的放在一塊,對應(yīng)的Linux時間戳從小到大進行排序.在編寫代碼之前可以想到,底層的MapReduce已經(jīng)幫我們做了一些工作:對key值相同進行聚集(shuffle洗牌).這里是看主要是reduce部分,map部分工作已經(jīng)簡化了.
剛開始的時候,我是這么想的:
map(k0,v0)-->list(k1,v1)
key: k1 是ID號, Value: v1 是時間戳
reduce(k1,list(v1)) -->list(k2,v2)
在reduce中對list(v1),也就是時間戳列表,先添加到list中,然后用Collections的sort方法對list進行排序,最后將結(jié)果進行輸出.程序如下:
public class SortReducer extends Reducer<Text,LongWritable,Text,LongWritable>{
@Override
protected void reduce(Text key, Iterable<LongWritable> values,
Context context) throws IOException, InterruptedException {
// TODO Auto-generated method stub
List<LongWritable> queue = new ArrayList<LongWritable>();
for(LongWritable num: values) {
queue.add(num);
}
Collections.sort(queue);//排序
//將排序結(jié)果輸出
for(LongWritable num:queue) {
context.write(key, num);
}
}
}
貌似邏輯沒有一點問題,可是結(jié)果卻是:
所有的時間都是一樣的.這是為什么呢?于是網(wǎng)上百度一番,終于找到原因了:
reduce方法會反復(fù)執(zhí)行多次嚼松,但key和value相關(guān)的對象只有兩個,reduce會反復(fù)重用這兩個對象屿愚。所以如果要保存key或者value的結(jié)果对妄,只能將其中的值取出另存或者重新clone一個對象(例如Text store = new Text(value) 或者 String a = value.toString())画切,而不能直接賦引用东涡。因為引用從始至終都是指向同一個對象隆嗅,你如果直接保存它們错忱,那最后它們都指向最后一個輸入記錄儡率。會影響最終計算結(jié)果而出錯。
解決方案:
String str = num.toString();
LongWritable num1 = new LongWritable(Long.parseLong(str));
參考資料:http://zhangqibuptse.iteye.com/blog/2071143
修改后的源代碼如下:
public class SortReducer extends Reducer<Text,LongWritable,Text,LongWritable>{
@Override
protected void reduce(Text key, Iterable<LongWritable> values,
Context context) throws IOException, InterruptedException {
// TODO Auto-generated method stub
List<LongWritable> queue = new ArrayList<LongWritable>();
for(LongWritable num: values) {
String str = num.toString();
LongWritable num1 = new LongWritable(Long.parseLong(str));
queue.add(num1);
}
Collections.sort(queue);
for(LongWritable num:queue) {
String str = num.toString();
LongWritable num1 = new LongWritable(Long.parseLong(str));
context.write(key, num1);
}
}
}
最終結(jié)果:
后文
這是我在大數(shù)據(jù)學(xué)習(xí)的個人總結(jié),如果有錯誤的地方或者還有改進的地方希望大家不吝賜教.