【大数据实验】使用hadoop实现倒排索引

零碎记事

  自上次写博客,已经过了半年了。

  这半年时间里,我退出了ACM,转入了计算机视觉的研究中,ACM的价值观与自己的价值观还是有所不符,感觉自己并不是那么适合一条路走到黑的竞赛,被束缚住了,还是诗与歌的生活更适合自己,能力低点又何妨,还是生活重要,所以转向压力稍低点(?)的科研项目里。

  退出ACM后,也停止了刷OJ题,以写题解为主的博客,理所当然地失去了写作源泉,一停,就是半年。

  说起来,当初转入计算机视觉研究的时候,还想着以后博客更新与这个领域相关的东西呢,可惜至今为止并没学到多少东西,仍然属于半吊子状态,实在是没内容拿得出手。

  那,为什么现在又更博客了呢?看标题,还是大数据领域的。

  这要跟本人目前就读的大数据专业说起,这个专业是学校新开的,而我们又是其第一届学生,学校对课程难度拿捏不太准,于是自开学后,发生了一系列离谱的事情,这里不吐不快。

  开学后,第一件事是考上学期遗留下来必须得线下考的试,我们专业在这场考试中,挂掉了60%以上的人。。。这只是个开端,之后学校排错了课程。大数据的实验课,需要java和Linux作为前置课程,而这两个课程又是我们这个学期的二选一选修课(没错,java跟Linux,本来我们这学期只学其中一个,个人也很疑惑java为什么会是选修。。。),怎么考虑本都是下学期才上的,但是不知怎么的排到了这个学期。然后教务处还发现java跟Linux是选修,就在第二周把这两门课紧急调整成了必修,课表课程喜加二(大数据实验 + 原本没选的java或Linux两者其一),于是这个学期12门课程,加上一个数据结构课设,做个游戏出来的那种。。。学校也是很厉害,这么多个课,居然也不冲突,可能我们专业就两个班,专业课都是两个班一起上,一个教室就能装得下,课表哪有空直接把课放哪就行了,好排。冲突是不冲突了,反人类的安排自然还是有的,就拿Java说事,Java有实验课,上这个实验课我们得跨校区去上,一般来讲,如果有两节连上的情况,那应该是放在同一个时间段比较合适,不用跑来跑去嘛。现实情况呢,一学期12门课的专业,有那么多选择的余地么,幺蛾子就这么来的,星期三有个连堂的java实验课,虽然连堂,连接的是上午跟下午,一节排在上午最后一节课,一节排在下午第一节课,就连Java老师都吐槽“我怎么看不懂你们专业的排课?”,大家笑而不语。提一下,我们星期三是全天满课从早上8点上到晚上9点多的,当天我们这专业的学生活动路线基本如下:宿舍早上起床->去宿舍校区的教室上课->骑羊跨校区上实验课->骑羊跨校区回宿舍校区吃饭睡午觉->起床又骑羊跨校区上实验课->骑羊跨校区会宿舍校区的教室上课->吃个晚饭,太累了,宿舍里歇会->去宿舍校区的教室上课->回宿舍。一天跨n次校区还不带休息,大家表示都很淦。还有其他操作,什么周一到周六早上第一节课天天有课,大数据实验课为什么不干脆跟java实验课排在一起,等等等等,都是我们大数据专业与其他专业的饭后谈资。

  听说学弟学妹们就好很多了,大一取消了数电课(好像我们一半的挂科率由这个科贡献的),另外大数据实验课正确地排到大二下学期,但是舒服都是学弟学妹们的,与我们无关。

  一学期12门课,当然很难顶,尤其是这个大数据实验课,一堆坑,是吃时间的大户,这学期时间本来就少,再来这么一手,连我这个有相当计算机基础的学生都感到了很大压力,预计这学期挂科率比上学期还高。

  但是眼睁睁看着自己专业挂科率一路攀升还是于心不忍,力所能及地做些自己能做到的事吧。。。

  这次开的大数据实验篇,主要面向自己专业的学生,让他们百度的时候至少可以百度出好点的参考材料。。。


题目:倒排索引

输入:多个文件,文件里都是一些单词

输出:要求使用MapReduce编程,统计文件单词,以N、M字母为界限进行分区,最终输出格式如下:

word1 inputfile1.txt->count1,inputfile2.txt->count2,...
word2 inputfile1.txt->count1,inputfile2.txt->count2,...
word3 inputfile1.txt->count1,inputfile2.txt->count2,...
...

 


思路:使用两次MapReduce,第一次MapReduce产生的文件是第二次MapReduce的输入

第一次MapReduce:使用WordCount,统计文件单词,outputKey是word->file.txt,outputValue是count

第二次MapReduce:

Map思路:k1 是Object, v1是每行数据word->file.txt \t count,k2是word,v2是file.txt->count

Reduce思路:k1,v1与Mapper的output一致,k2是word,v2是file1.txt->count1,file2.txt->count2,......


代码:

第一次MapReduce(WordCount)的代码:

MainClass

 1 package WordCount;
 2 
 3 import java.io.IOException;
 4 import java.net.URI;
 5 
 6 import org.apache.hadoop.conf.Configuration;
 7 import org.apache.hadoop.fs.FileSystem;
 8 import org.apache.hadoop.fs.Path;
 9 import org.apache.hadoop.io.IntWritable;
10 import org.apache.hadoop.io.Text;
11 import org.apache.hadoop.mapreduce.Job;
12 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
13 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
14 
15 public class MainClass {
16     public static void main(String[] args) throws IOException, Exception {
17         Configuration conf = new Configuration();
18         Job job = Job.getInstance(conf, "Word Count");
19         
20         if(args.length != 2) {
21             System.err.println("两个参数 <input> <output>");
22             System.exit(2);
23         }
24         
25         final FileSystem fileSystem = FileSystem.get(new URI(args[0]), conf);
26         if(fileSystem.exists(new Path(args[1])))
27             fileSystem.delete(new Path(args[1]), true);
28         
29         job.setJarByClass(MainClass.class);
30         
31         job.setMapperClass(WCMapper.class);
32         job.setMapOutputKeyClass(Text.class);
33         job.setMapOutputValueClass(IntWritable.class);
34         
35         job.setCombinerClass(WCReducer.class);
36         
37         job.setPartitionerClass(WCPartitioner.class);
38         job.setNumReduceTasks(3);
39         
40         job.setReducerClass(WCReducer.class);
41         job.setOutputKeyClass(Text.class);
42         job.setOutputValueClass(IntWritable.class);
43         
44         FileInputFormat.setInputPaths(job, new Path(args[0]));
45         FileOutputFormat.setOutputPath(job, new Path(args[1]));
46         
47         job.waitForCompletion(true);
48     }
49 }

 

Mapper

 1 package WordCount;
 2 
 3 import org.apache.hadoop.mapreduce.Mapper;
 4 import org.apache.hadoop.mapreduce.lib.input.FileSplit;
 5 import org.apache.hadoop.io.Text;
 6 
 7 import java.io.IOException;
 8 
 9 import org.apache.hadoop.fs.Path;
10 import org.apache.hadoop.io.IntWritable;
11 
12 public class WCMapper extends Mapper<Object, Text, Text, IntWritable> {
13 
14     @Override
15     protected void map(Object key, Text value, Mapper<Object, Text, Text, IntWritable>.Context context)
16             throws IOException, InterruptedException {
17         String line = value.toString().trim();
18         String[] words = line.split(" ");
19         FileSplit inputSplit = (FileSplit)context.getInputSplit();
20         Path path = inputSplit.getPath();
21         String filename = path.getName();
22         for(String word : words)
23             context.write(new Text(word + "->" + filename), new IntWritable(1));
24     }
25     
26 }

 

Partitioner

 1 package WordCount;
 2 
 3 import org.apache.hadoop.mapreduce.Partitioner;
 4 import org.apache.hadoop.io.Text;
 5 import org.apache.hadoop.io.IntWritable;
 6 public class WCPartitioner extends Partitioner<Text, IntWritable> {
 7 
 8     @Override
 9     public int getPartition(Text key, IntWritable value, int numPartitions) {
10         char firstCharacter = Character.toUpperCase(key.toString().charAt(0));
11         
12         if(firstCharacter >= 'A' && firstCharacter <= 'M') return 1;
13         if(firstCharacter >= 'N' && firstCharacter <= 'Z') return 2;
14         return 0;
15     }
16     
17 }

 

Reducer

 1 package WordCount;
 2 
 3 import org.apache.hadoop.mapreduce.Reducer;
 4 import org.apache.hadoop.io.Text;
 5 
 6 import java.io.IOException;
 7 
 8 import org.apache.hadoop.io.IntWritable;
 9 public class WCReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
10 
11     @Override
12     protected void reduce(Text key, Iterable<IntWritable> value,
13             Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
14         int sum = 0;
15         for(IntWritable i : value)
16             sum += i.get();
17         
18         context.write(key, new IntWritable(sum));
19     }
20     
21 }

 

第二次MapReduce的代码

MainClass

package Sort;

import java.io.IOException;
import java.net.URI;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class SortMain {
    public static void main(String[] args) throws IOException, Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "Sort Index");
        
        if(args.length != 2) {
            System.err.println("两个参数 <input> <output>");
            System.exit(2);
        }
        
        final FileSystem fileSystem = FileSystem.get(new URI(args[0]), conf);
        if(fileSystem.exists(new Path(args[1])))
            fileSystem.delete(new Path(args[1]), true);
        
        job.setJarByClass(SortMain.class);
        
        job.setMapperClass(SortMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);
        
        job.setPartitionerClass(SortPartitioner.class);
        job.setNumReduceTasks(3);
        
        job.setReducerClass(SortReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);
        
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        
        job.waitForCompletion(true);
    }
}

 

Mapper

 1 package Sort;
 2 
 3 import org.apache.hadoop.mapreduce.Mapper;
 4 
 5 import java.io.IOException;
 6 
 7 import org.apache.hadoop.io.Text;
 8 
 9 public class SortMapper extends Mapper<Object, Text, Text, Text> {
10 
11     @Override
12     protected void map(Object key, Text value, Mapper<Object, Text, Text, Text>.Context context)
13             throws IOException, InterruptedException {
14         String line = value.toString().trim();
15         if(line.length() == 0) return;
16         String[] str1 = line.split("\t");
17         String[] str2 = str1[0].split("->");
18         String word = str2[0];
19         String file = str2[1];
20         String num = str1[1];
21         
22         context.write(new Text(word), new Text(file + "->" + num));
23     }
24 
25 }

Partitioner

 1 package Sort;
 2 
 3 import org.apache.hadoop.io.Text;
 4 import org.apache.hadoop.mapreduce.Partitioner;
 5 
 6 public class SortPartitioner extends Partitioner<Text, Text> {
 7 
 8     @Override
 9     public int getPartition(Text key, Text value, int numPartitions) {
10         char firstCharacter = Character.toUpperCase(key.toString().charAt(0));
11         
12         if(firstCharacter >= 'A' && firstCharacter <= 'M') return 1;
13         if(firstCharacter >= 'N' && firstCharacter <= 'Z') return 2;
14         return 0;
15     }
16 
17 }

Reducer

 1 package Sort;
 2 
 3 import org.apache.hadoop.mapreduce.Reducer;
 4 
 5 import java.io.IOException;
 6 
 7 import org.apache.hadoop.io.Text;
 8 
 9 public class SortReducer extends Reducer<Text, Text, Text, Text> {
10 
11     @Override
12     protected void reduce(Text key, Iterable<Text> value, Reducer<Text, Text, Text, Text>.Context context)
13             throws IOException, InterruptedException {
14         String v2 = "";
15         for(Text v1 : value)
16             v2 += v1.toString() + ",";
17         v2 = v2.substring(0, v2.length()-1);
18         
19         context.write(key, new Text(v2));
20     }
21 
22 }

实验过程

输入文件有3个

words1.txt

 words2.txt

words3.txt

接下来要进行两次MapReduce操作,为执行方便,我写了个shell脚本,shell会按照脚本内容逐条运行指令,调试起来就很方便

 

 

 

 

 运行脚本(实际上就是按顺序运行上面两条红线的指令)

 

 

 整个过程非常顺利

 

 

 看看输出路径下的文件,确实没问题

 

 

 先看看第一次MapReduce的结果

 

 

 

 

是WordCount没错了,我们看看这基础之上第二次MapReduce的结果

 

 

 

 

 

 可以看到并没有什么问题,这就是倒排索引的整个实验过程了。


写程序不一定一次就能成功的,大家可以看到前边有很多其他的output文件,都是有问题的= =,后来慢慢调试到成功能展示出来罢了

 

posted @ 2020-12-04 10:24  雾里尘埃  阅读(894)  评论(0)    收藏  举报