1.5MapReduce 实例:去重(Hadoop3.0)
1.5MapReduce 实例:去重(Hadoop3.0)
【实验目的】
1.准确理解MapReduce去重的设计原理
2.熟练掌握MapReduce去重的程序编写
3.学会自己编写MapReduce去重代码解决实际问题
【实验原理】
“数据去重”主要是为了掌握和利用并行化思想来对数据进行有意义的筛选。统计大数据集上的数据种类个数、从网站日志中计算访问地等这些看似庞杂的任务都会涉及数据去重。
数据去重的最终目标是让原始数据中出现次数超过一次的数据在输出文件中只出现一次。在MapReduce流程中,map的输出<key,value>经过shuffle过程聚集成<key,value-list>后交给reduce。我们自然而然会想到将同一个数据的所有记录都交给一台reduce机器,无论这个数据出现多少次,只要在最终结果中输出一次就可以了。具体就是reduce的输入应该以数据作为key,而对value-list则没有要求(可以设置为空)。当reduce接收到一个<key,value-list>时就直接将输入的key复制到输出的key中,并将value设置成空值,然后输出<key,value>。
MaprReduce去重流程如下图所示:
【实验环境】
Kylin V10 SP1
Java 1.8.0
Hadoop-3.0.0
Eclipse-JEE 2022.03
【实验内容】
现有一个某电商网站的数据文件,名为buyer_favorite1,记录了用户收藏的商品以及收藏的日期,文件buyer_favorite1中包含(用户id,商品id,收藏日期)三个字段,数据内容以“\t”分割,由于数据很大,所以为了方便统计我们只截取它的一部分数据,内容如下:
用户id 商品id 收藏日期 10181 1000481 2010-04-04 16:54:31 20001 1001597 2010-04-07 15:07:52 20001 1001560 2010-04-07 15:08:27 20042 1001368 2010-04-08 08:20:30 20067 1002061 2010-04-08 16:45:33 20056 1003289 2010-04-12 10:50:55 20056 1003290 2010-04-12 11:57:35 20056 1003292 2010-04-12 12:05:29 20054 1002420 2010-04-14 15:24:12 20055 1001679 2010-04-14 19:46:04 20054 1010675 2010-04-14 15:23:53 20054 1002429 2010-04-14 17:52:45 20076 1002427 2010-04-14 19:35:39 20054 1003326 2010-04-20 12:54:44 20056 1002420 2010-04-15 11:24:49 20064 1002422 2010-04-15 11:35:54 20056 1003066 2010-04-15 11:43:01 20056 1003055 2010-04-15 11:43:06 20056 1010183 2010-04-15 11:45:24 20056 1002422 2010-04-15 11:45:49 20056 1003100 2010-04-15 11:45:54 20056 1003094 2010-04-15 11:45:57 20056 1003064 2010-04-15 11:46:04 20056 1010178 2010-04-15 16:15:20 20076 1003101 2010-04-15 16:37:27 20076 1003103 2010-04-15 16:37:05 20076 1003100 2010-04-15 16:37:18 20076 1003066 2010-04-15 16:37:31 20054 1003103 2010-04-15 16:40:14 20054 1003100 2010-04-15 16:40:16 |
要求用Java编写MapReduce程序,根据商品id进行去重,统计用户收藏商品中都有哪些商品被收藏。结果数据如下:
商品id 1000481 1001368 1001560 1001597 1001679 1002061 1002420 1002422 1002427 1002429 1003055 1003064 1003066 1003094 1003100 1003101 1003103 1003289 1003290 1003292 1003326 1010178 1010183 1010675 |
【实验步骤】
打开终端模拟器,切换到vmuser 用户(密码:vm123456)
su - vmuser
1切换到/apps/hadoop/sbin目录下,开启Hadoop。
cd /apps/hadoop/sbin
./start-all.sh
使用jps查看启动的进程
jps
2.在Linux本地新建/data1/mapreduce2目录。
3.在Linux中切换到/data1/mapreduce2目录下,用wget命令下载文本文件buyer_favorite2。
cd /data1/mapreduce2
wget http://mapreduce2/buyer_favorite2
然后在当前目录下用wget命令下载项目用到的依赖包。
wget http://mapreduce2/hadoop2lib.tar.gz
将hadoop2lib.tar.gz解压到当前目录下。
tar zxvf hadoop2lib.tar.gz
4.首先在HDFS上新建/mymapreduce2/in目录,然后将Linux本地/data1/mapreduce1目录下的buyer_favorite2文件导入到HDFS的/mymapreduce2/in目录中。
hadoop fs -mkdir -p /mymapreduce2/in
hadoop fs -put /data1/mapreduce1/buyer_favorite2 /mymapreduce2/in
5.双击Eclipse,默认/home/devuser/eclipse-workspace路径,点击【Launch】,点击【File】---->【New】---->【Project】
项目名为mapreduce2,JRE选择【JavaSE-1.8】,点击【Finish】
在mapreduce2项目下新建包,点击【File】---->【New】---->【Package】
在mapreduce包下新建类,点击【mapreduce】右击,【New】---->【Class】
类名为Filter。
6.添加项目所需依赖的jar包,右键项目,新建一个文件夹,点击项目名mapreduce2,右击,点击【New】--->【Folder】
命名为:hadoop2lib,用于存放项目所需的jar包。
将/data/mapreduce2/hadoop2lib目录中的jar包,拷贝到Eclipse中mapreduce2项目的hadoop2lib目录下。
选中所有项目hadoop2lib目录下所有jar包,并添加到Build Path中。
7.编写程序代码,并描述其思路
数据去重的目的是让原始数据中出现次数超过一次的数据在输出文件中只出现一次。我们自然想到将相同key值的所有value记录交到一台reduce机器,让其无论这个数据出现多少次,最终结果只输出一次。具体就是reduce的输出应该以数据作为key,而对value-list没有要求,当reduce接收到一个时,就直接将key复制到输出的key中,将value设置为空。
public static class Map extends Mapper<Object , Text , Text , NullWritable> //map将输入中的value复制到输出数据的key上,并直接输出 { private static Text newKey=new Text(); //从输入中得到的每行的数据的类型 public void map(Object key,Text value,Context context) throws IOException, InterruptedException //实现map函数 { //获取并输出每一次的处理过程 String line=value.toString(); System.out.println(line); String arr[]=line.split("\t"); newKey.set(arr[1]); context.write(newKey, NullWritable.get()); System.out.println(newKey); } } |
map阶段采用Hadoop的默认的作业输入方式,把输入的value用split()方法截取,截取出的商品id字段设置为key,设置value为空,然后直接输出<key,value>。
reduce端代码
public static class Reduce extends Reducer<Text, NullWritable, Text, NullWritable>{ public void reduce(Text key,Iterable<NullWritable> values,Context context) throws IOException, InterruptedException //实现reduce函数 { context.write(key,NullWritable.get()); //获取并输出每一次的处理过程 } } |
map输出的<key,value>键值对经过shuffle过程,聚成<key,value-list>后,会交给reduce函数。reduce函数,不管每个key 有多少个value,它直接将输入的值赋值给输出的key,将输出的value设置为空,然后输出<key,value>就可以了。
完整代码
package mapreduce; import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.NullWritable; 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.input.TextInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; public class Filter { public static class Map extends Mapper<Object, Text, Text, NullWritable> { private static Text newKey = new Text(); public void map(Object key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); System.out.println(line); String arr[] = line.split("\t"); newKey.set(arr[1]); context.write(newKey, NullWritable.get()); System.out.println(newKey); } } public static class Reduce extends Reducer<Text, NullWritable, Text, NullWritable> { public void reduce(Text key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException { context.write(key, NullWritable.get()); } } public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration conf = new Configuration(); System.out.println("start"); Job job = new Job(conf, "filter"); job.setJarByClass(Filter.class); job.setMapperClass(Map.class); job.setReducerClass(Reduce.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(NullWritable.class); job.setInputFormatClass(TextInputFormat.class); job.setOutputFormatClass(TextOutputFormat.class); Path in = new Path("hdfs://localhost:9000/mymapreduce2/in/buyer_favorite2"); Path out = new Path("hdfs://localhost:9000/mymapreduce2/out"); FileInputFormat.addInputPath(job, in); FileOutputFormat.setOutputPath(job, out); System.exit(job.waitForCompletion(true) ? 0 : 1); } } |
8.在Filter类文件中,右键并点击Run As => Run on Hadoop选项,将MapReduce任务提交到Hadoop中。
注意in 和 out 文件位置
9.待执行完毕后,进入命令模式下,在HDFS中/mymapreduce2/out查看实验结果。
hadoop fs -ls /mymapreduce2/out
hadoop fs -cat /mymapreduce2/out/part-r-00000
将HDFS上的输出结果下载到本地
hadoop fs -get /mymapreduce2/out/part-r-00000 /data1/mapreduce1/
ls
cat part-r-00000
至此,本实验结束!