原理
如果A和B具有好友關系,B和C具有好友關系,而A和C卻不是好友關系,那么我們稱A和C這樣的關系為:二度好友關系。
在生活中,二度好友推薦的運用非常廣泛,比如某些主流社交產品中都會有"可能認識的人"這樣的功能,一般來說可能認識的人就是通過二度好友關系搜索得到的,在傳統的關系型數據庫中,可以通過圖的廣度優先遍歷算法實現,而且深度限定為2,然而在海量的數據中,這樣的遍歷成本太大,所以有必要利用MapReduce編程模型來並行化。
初始數據如下:
A B
C D
E F
F G
B D
B C
map階段得到的結果為:
Key:A Value:B
Key:B Value:A C D
Key:C Value:B D
Key:E Value:F
Key:F Value:E G
Key:G Value:F
Reduce階段再將Value進行笛卡爾積運算就可以得到二度好友關系了
(笛卡爾積公式:A×B={(x,y)|x∈A∧y∈B}
例如,A={a,b}, B={0,1,2},則
A×B={(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}
B×A={(0, a), (0, b), (1, a), (1, b), (2, a), (2, b)})
環境
Linux Ubuntu 14.04
jdk-7u75-linux-x64
Hadoop 2.6.0-cdh5.4.5
內容
通過初始數據,假設有A、B、C、D、E、F、G七位同學,其中A與B是好友關系,C與D是好友關系,E與F是好友關系,F與G是好友關系,B與D是好友關系,B與C是好友關系,通過分析A與B是好友,且B與C也是好友,我們就認為A與C互為可能認識的人,向A與C互相推薦對方。
實驗步驟
1.首先,來准備實驗需要用到的數據,切換到/data/mydata目錄下,使用vim編輯一個friend_data.txt文件。
-
cd /data/mydata
-
vim friend_data.txt
2.將如下初始數據寫入其中(注意數據之間以空格分割)
-
A B
-
C D
-
E F
-
F G
-
B D
-
B C
3.切換到/apps/hadoop/sbin目錄下,開啟Hadoop相關進程
-
cd /apps/hadoop/sbin
-
./start-all.sh
4.輸入JPS查看一下相關進程是否已經啟動。
-
jps
5.在HDFS的根下創建一個friend目錄,並將friend_data.txt文件上傳到HDFS上的friend文件夾下。
-
hadoop fs -mkdir /friend
-
hadoop fs -put /data/mydata/friend_data.txt /friend
6.打開Eclipse,創建一個Map/Reduce項目。
7.設置項目名為mr_sf並點擊Finish。
8.創建一個包,名為mr_friend。
9.創建一個類,名為Find_Friend。
10.下面開始編寫Find_Friend類的代碼。
完整代碼為:
-
package mr_friend;
-
import java.io.IOException;
-
import java.net.URI;
-
import java.net.URISyntaxException;
-
import java.util.HashSet;
-
import java.util.Iterator;
-
import java.util.Set;
-
-
import org.apache.hadoop.conf.Configuration;
-
import org.apache.hadoop.fs.FileSystem;
-
import org.apache.hadoop.fs.Path;
-
import org.apache.hadoop.io.LongWritable;
-
import org.apache.hadoop.io.Text;
-
import org.apache.hadoop.mapreduce.Job;
-
import org.apache.hadoop.mapreduce.Mapper;
-
import org.apache.hadoop.mapreduce.Reducer;
-
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
-
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
-
-
public class Find_Friend {
-
/*
-
map結果:
-
A B
-
B A
-
C D
-
D C
-
E F
-
F E
-
F G
-
G F
-
B D
-
D B
-
B C
-
C B
-
*/
-
-
public static class FindFriendsMapper extends Mapper<LongWritable, Text, Text, Text> {
-
@Override
-
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, Text>.Context context)
-
throws IOException, InterruptedException {
-
String line = value.toString();
-
String array[] = line.split("\\s+");
-
context.write(new Text(array[0]), new Text(array[1]));
-
context.write(new Text(array[1]), new Text(array[0]));
-
}
-
}
-
-
/*
-
map之后,Shuffling將相同key的整理在一起,結果如下:
-
shuffling結果(將結果輸出到reduce):
-
A B
-
-
B A
-
B D
-
B C
-
-
C D
-
C B
-
-
E F
-
-
F E
-
F G
-
-
G F
-
*/
-
//reduce將上面的數據進行笛卡爾積計算
-
public static class FindFriendsReduce extends Reducer<Text, Text, Text, Text> {
-
@Override
-
protected void reduce(Text key, Iterable<Text> values, Reducer<Text, Text, Text, Text>.Context context)
-
throws IOException, InterruptedException {
-
//將重復數據去重
-
Set<String> set = new HashSet<String>();
-
for (Text v : values) {
-
set.add(v.toString());
-
}
-
-
if (set.size() > 1) {
-
for (Iterator<String> i = set.iterator(); i.hasNext();) {
-
String qqName = i.next();
-
for (Iterator<String> j = set.iterator(); j.hasNext();) {
-
String otherQqName = j.next();
-
if (!qqName.equals(otherQqName)) {
-
context.write(new Text(qqName), new Text(otherQqName));
-
}
-
}
-
}
-
}
-
}
-
}
-
-
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException, URISyntaxException {
-
final String INPUT_PATH = "hdfs://127.0.0.1:9000/friend/friend_data.txt";
-
final String OUTPUT_PATH = "hdfs://127.0.0.1:9000/friend/output";
-
-
Configuration conf = new Configuration();
-
//Configuration:map/reduce的配置類,向hadoop框架描述map-reduce執行的工作
-
-
final FileSystem fileSystem = FileSystem.get(new URI(INPUT_PATH), conf);
-
if(fileSystem.exists(new Path(OUTPUT_PATH))) {
-
fileSystem.delete(new Path(OUTPUT_PATH), true);
-
}
-
-
Job job = Job.getInstance(conf, "Find_Friend");//設置一個用戶定義的job名稱
-
job.setJarByClass(Find_Friend.class);
-
job.setMapperClass(FindFriendsMapper.class); //為job設置Mapper類
-
job.setReducerClass(FindFriendsReduce.class); //為job設置Reducer類
-
job.setOutputKeyClass(Text.class); //為job的輸出數據設置Key類
-
job.setOutputValueClass(Text.class); //為job輸出設置value類
-
-
FileInputFormat.addInputPath(job, new Path(INPUT_PATH));
-
FileOutputFormat.setOutputPath(job, new Path(OUTPUT_PATH));
-
-
System.exit(job.waitForCompletion(true) ?0 : 1); //運行job
-
}
-
-
}
11.下面在Find_Friend類下,單擊右鍵,選擇Run As=>Run on Hadoop,運行程序,查看執行結果。
12.程序執行完以后,查看HDFS上的/friend/output目錄中的計算結果。
-
hadoop fs -ls -R /friend
-
hadoop fs -cat /friend/output/part-r-00000
通過分析結果,就得出了各位同學的可能認識的人的列表了。
至此,實驗就已經結束了。












