怎么利用Hadoop实现求共同好友
怎么利用Hadoop实现求共同好友
这篇文章主要介绍怎么利用Hadoop实现求共同好友,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
前言
在很多社交APP中,比如大家熟悉的QQ好友列表中,打开会话框,经常可以看到下面有一栏共同好友的推荐列表,用户通过这种方式,可以添加潜在的关联好友
这种功能该如何实现呢?对redis比较了解的同学应该能很快想到,可以使用redis来实现这个功能。没错,redis确实是个不错的可以实现这个功能的方案。
但redis的实现有一定的局限性,因为redis存储和数据和计算时需要耗费较多的内存资源,设想一下,像腾讯QQ这样的规模,如果用这种方式做的话,估计Redis服务器的投入成本将是一笔不小的开销。
利用hadoop中的MapReduce同样可以实现这个功能,该如何实现呢?
业务分析
下面是原始的数据文件,第一栏可理解为本人,第二行为该用户的好友列表,以逗号分割,比如A用户的好友包括:B,C,D,F,E,O这几个,后面的行依次类推
A:B,C,D,F,E,O
B:A,C,E,K
C:F,A,D,I
D:A,E,F,L
E:B,C,D,M,L
F:A,B,C,D,E,O,M
G:A,C,D,E,F
H:A,C,D,E,O
I:A,O
J:B,O
K:A,C,D
L:D,E,F
M:E,F,G
O:A,H,I,J
现在的需求是:通过原始的数据文件,输出该文件中所有用户中哪些人两两之间存在共同好友并输出,格式如下:
A-B C,E
A-C F,D
A-D E,F
......
实现思路分析
步骤一:将原始数据拆分为如下格式
通过这一步,得到一组K/V,可以清晰的反映出一个用户的所有好友
B:A #B是A的好友
C:A #C是A的好友
D:A #D是A的好友
F:A
E:A
O:AA:B
C:B
E:B
K:BF:C
A:C
D:C
I:CB:E
C:E
D:E
M:E
L:E
步骤二、对第一步的数据进一步处理成如下格式
从第一步格式完毕后的数据,可以很明显看出并总结出一个规律,那就是左边那些用户的好友列表,以C用户为例,可以看出C这个用户有A,B,E三个好友,反过来讲,ABE这三个用户,他们有一个共同的好友A
其他的类推进行理解
C A-B-E #C是A和B和E的共同好友
D A-C #D是A和B的共同好友
A B-C #A是B和C的共同好友
B A-E #A是E和B的共同好友
......
步骤三、将步骤二中的数据调换位置
从步骤2中我们得知,C的好友有ABE,反过来说,ABE他们的共同好友有C,针对这种超过3个的,可以考虑下一步进行两两组合即可
A-B-E C #A、B、E有共同好友C
A-C D #A与C有共同好友D
B-C A #B与C有共同好友A
A-E B #A与E有共同好友B
步骤四、将步骤三得到的数据继续拆分
步骤三中,像 : A-B-E C 这种数据,显然需要进一步拆分,因为最终的结果是求取两两好友之间的共同好友,所以可以拆为: A-B C,A-E C,B-E C,为下一步数据组合做最后的准备
A-B C
A-E C
B-E C
A-C D
B-C A
A-E B
......
步骤五、将步骤四得到的数据合并
在使用MapReduce编程中我们知道,Map阶段出去的数据,进入reduce方法中的数据都是key相同的,以第四步中的: A-E 这个key为例,就有2个,这样通过 reduce方法最终输出的结果就是: A-E C,B ,即A-E 这两个用户的共同好友为 C和B
A-B C #A,B共同好友有C
A-E C,B #A,E有共同好友 C,B
B-E C #B,E有共同好友 C
A-C D #A,C有共同好友 D
B-C A #B,C有共同好友 A
......
通过以上的数据分析,最终可以达到预期的效果,同时也可以看出,上面的步骤划分到MapRedcue中,显然一个MapReduce肯定是无法完成的,至少需要2个
下面是结合上面的步骤分析,得出需要两个MapReduce的数据流程图,参考这个图来协助我们分析编写代码逻辑做参考
编码实现
1、第一个map类
publicclassFirstMapperextendsMapper<LongWritable,Text,Text,Text>{@Overrideprotectedvoidmap(LongWritablekey,Textvalue,Contextcontext)throwsIOException,InterruptedException{Stringval=value.toString();String[]split=val.split(":");//A:B,C,D,F,E,O拆分后,左边是原用户,右边是好友Stringuser=split[0];Stringfriends=split[1];String[]friendLists=friends.split(",");//Map1输出的结果为:/***BA*CA*DA*FA*EA*/for(Stringstr:friendLists){context.write(newText(str),newText(user));}}}
2、第一个Reduce类
publicclassFirstReducerextendsReducer<Text,Text,Text,Text>{@Overrideprotectedvoidreduce(Textkey,Iterable<Text>values,Contextcontext)throwsIOException,InterruptedException{StringBufferstringBuffer=newStringBuffer();for(Texttext:values){stringBuffer.append(text).append("-");}//最终写出去的数据格式为:A-EB......context.write(newText(stringBuffer.toString()),key);}}
3、第一个Job类
publicclassFirstJob{publicstaticvoidmain(String[]args)throwsException{//1、获取jobConfigurationconfiguration=newConfiguration();Jobjob=Job.getInstance(configuration);//2、设置jar路径job.setJarByClass(FirstJob.class);//3、关联mapper和Reducerjob.setMapperClass(FirstMapper.class);job.setReducerClass(FirstReducer.class);//4、设置map输出的key/val的类型job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(Text.class);//5、设置最终输出的key/val类型job.setOutputKeyClass(Text.class);job.setOutputValueClass(Text.class);//6、设置最终的输出路径StringinputPath="F:\\网盘\\csv\\friends.txt";StringoutPath="F:\\网盘\\csv\\friends1";FileInputFormat.setInputPaths(job,newPath(inputPath));FileOutputFormat.setOutputPath(job,newPath(outPath));//7提交jobbooleanresult=job.waitForCompletion(true);System.exit(result?0:1);}}
运行上面的Job代码,然后打开运行完毕后的第一个阶段的文件,从内容格式上看,符合第一阶段的输出结果要求的, 即下面的这种数据格式
4、第二个map类
publicclassSecondMapperextendsMapper<LongWritable,Text,Text,Text>{@Overrideprotectedvoidmap(LongWritablekey,Textvalue,Contextcontext)throwsIOException,InterruptedException{//I-K-C-B-G-F-H-O-D-A阶段1的文件输出格式/***最终输出格式:*I-KA*I-CA*I-BA*......*///需要将左边的数据进行两两拆分,与V进行组合输出Stringval=value.toString();String[]split=val.split("\t");Stringv2=split[1];String[]allUsers=split[0].split("-");Arrays.sort(allUsers);for(inti=0;i<allUsers.length-1;i++){for(intj=i+1;j<allUsers.length;j++){context.write(newText(allUsers[i]+"-"+allUsers[j]),newText(v2));}}}}
5、第二个Reducer类
publicclassSecondReducerextendsReducer<Text,Text,Text,Text>{@Overrideprotectedvoidreduce(Textkey,Iterable<Text>values,Contextcontext)throwsIOException,InterruptedException{//上一步输出的结果:/***A-BC*A-BD*A-EC*A-ED*......*///只需要将相同的key的Val进行组合即可,即:A-BC-D,A-EC-DStringBufferstringBuffer=newStringBuffer();for(Texttext:values){stringBuffer.append(text.toString()).append("-");}context.write(key,newText(stringBuffer.toString()));}}
6、第二个Job类
publicclassSecondJob{publicstaticvoidmain(String[]args)throwsException{//1、获取jobConfigurationconfiguration=newConfiguration();Jobjob=Job.getInstance(configuration);//2、设置jar路径job.setJarByClass(SecondJob.class);//3、关联mapper和Reducerjob.setMapperClass(SecondMapper.class);job.setReducerClass(SecondReducer.class);//4、设置map输出的key/val的类型job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(Text.class);//5、设置最终输出的key/val类型job.setOutputKeyClass(Text.class);job.setOutputValueClass(Text.class);//6、设置最终的输出路径StringinputPath="F:\\网盘\\csv\\friends1\\part-r-00000";StringoutPath="F:\\网盘\\csv\\friends2";FileInputFormat.setInputPaths(job,newPath(inputPath));FileOutputFormat.setOutputPath(job,newPath(outPath));//7提交jobbooleanresult=job.waitForCompletion(true);System.exit(result?0:1);}}
运行上面的Job代码,查看最终的输出结果,可以看到,也是符合我们预期的业务的
以上是“怎么利用Hadoop实现求共同好友”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注恰卡编程网行业资讯频道!